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#------------------------------------------------------------------------------
41
42package Image::ExifTool::QuickTime;
43
44use strict;
45use vars qw($VERSION $AUTOLOAD %stringEncoding);
46use Image::ExifTool qw(:DataAccess :Utils);
47use Image::ExifTool::Exif;
48use Image::ExifTool::GPS;
49
50$VERSION = '2.68';
51
52sub ProcessMOV($$;$);
53sub ProcessKeys($$$);
54sub ProcessMetaKeys($$$);
55sub ProcessMetaData($$$);
56sub ProcessEncodingParams($$$);
57sub ProcessSampleDesc($$$);
58sub ProcessHybrid($$$);
59sub ProcessRights($$$);
60# ++vvvvvvvvvvvv++ (in QuickTimeStream.pl)
61sub Process_mebx($$$);
62sub Process_3gf($$$);
63sub Process_gps0($$$);
64sub Process_gsen($$$);
65sub ProcessRIFFTrailer($$$);
66sub ProcessTTAD($$$);
67sub ProcessNMEA($$$);
68sub ProcessGPSLog($$$);
69sub SaveMetaKeys($$$);
70# ++^^^^^^^^^^^^++
71sub ParseItemLocation($$);
72sub ParseContentDescribes($$);
73sub ParseItemInfoEntry($$);
74sub ParseItemPropAssoc($$);
75sub FixWrongFormat($);
76sub GetMatrixStructure($$);
77sub ConvertISO6709($);
78sub ConvInvISO6709($);
79sub ConvertChapterList($);
80sub PrintChapter($);
81sub PrintGPSCoordinates($);
82sub PrintInvGPSCoordinates($);
83sub UnpackLang($;$);
84sub WriteKeys($$$);
85sub WriteQuickTime($$$);
86sub WriteMOV($$);
87sub GetLangInfo($$);
88sub CheckQTValue($$$);
89
90# MIME types for all entries in the ftypLookup with file extensions
91# (defaults to 'video/mp4' if not found in this lookup)
92my %mimeLookup = (
93   '3G2' => 'video/3gpp2',
94   '3GP' => 'video/3gpp',
95    AAX  => 'audio/vnd.audible.aax',
96    DVB  => 'video/vnd.dvb.file',
97    F4A  => 'audio/mp4',
98    F4B  => 'audio/mp4',
99    JP2  => 'image/jp2',
100    JPM  => 'image/jpm',
101    JPX  => 'image/jpx',
102    M4A  => 'audio/mp4',
103    M4B  => 'audio/mp4',
104    M4P  => 'audio/mp4',
105    M4V  => 'video/x-m4v',
106    MOV  => 'video/quicktime',
107    MQV  => 'video/quicktime',
108    HEIC => 'image/heic',
109    HEVC => 'image/heic-sequence',
110    HEICS=> 'image/heic-sequence',
111    HEIF => 'image/heif',
112    HEIFS=> 'image/heif-sequence',
113    AVIF => 'image/avif', #PH (NC)
114    CRX  => 'video/x-canon-crx',    # (will get overridden)
115);
116
117# look up file type from ftyp atom type, with MIME type in comment if known
118# (ref http://www.ftyps.com/)
119my %ftypLookup = (
120    '3g2a' => '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-0 V1.0', # video/3gpp2
121    '3g2b' => '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-A V1.0.0', # video/3gpp2
122    '3g2c' => '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-B v1.0', # video/3gpp2
123    '3ge6' => '3GPP (.3GP) Release 6 MBMS Extended Presentations', # video/3gpp
124    '3ge7' => '3GPP (.3GP) Release 7 MBMS Extended Presentations', # video/3gpp
125    '3gg6' => '3GPP Release 6 General Profile', # video/3gpp
126    '3gp1' => '3GPP Media (.3GP) Release 1 (probably non-existent)', # video/3gpp
127    '3gp2' => '3GPP Media (.3GP) Release 2 (probably non-existent)', # video/3gpp
128    '3gp3' => '3GPP Media (.3GP) Release 3 (probably non-existent)', # video/3gpp
129    '3gp4' => '3GPP Media (.3GP) Release 4', # video/3gpp
130    '3gp5' => '3GPP Media (.3GP) Release 5', # video/3gpp
131    '3gp6' => '3GPP Media (.3GP) Release 6 Basic Profile', # video/3gpp
132    '3gp6' => '3GPP Media (.3GP) Release 6 Progressive Download', # video/3gpp
133    '3gp6' => '3GPP Media (.3GP) Release 6 Streaming Servers', # video/3gpp
134    '3gs7' => '3GPP Media (.3GP) Release 7 Streaming Servers', # video/3gpp
135    'aax ' => 'Audible Enhanced Audiobook (.AAX)', #PH
136    'avc1' => 'MP4 Base w/ AVC ext [ISO 14496-12:2005]', # video/mp4
137    'CAEP' => 'Canon Digital Camera',
138    'caqv' => 'Casio Digital Camera',
139    'CDes' => 'Convergent Design',
140    'da0a' => 'DMB MAF w/ MPEG Layer II aud, MOT slides, DLS, JPG/PNG/MNG images',
141    'da0b' => 'DMB MAF, extending DA0A, with 3GPP timed text, DID, TVA, REL, IPMP',
142    'da1a' => 'DMB MAF audio with ER-BSAC audio, JPG/PNG/MNG images',
143    'da1b' => 'DMB MAF, extending da1a, with 3GPP timed text, DID, TVA, REL, IPMP',
144    'da2a' => 'DMB MAF aud w/ HE-AAC v2 aud, MOT slides, DLS, JPG/PNG/MNG images',
145    'da2b' => 'DMB MAF, extending da2a, with 3GPP timed text, DID, TVA, REL, IPMP',
146    'da3a' => 'DMB MAF aud with HE-AAC aud, JPG/PNG/MNG images',
147    'da3b' => 'DMB MAF, extending da3a w/ BIFS, 3GPP timed text, DID, TVA, REL, IPMP',
148    'dmb1' => 'DMB MAF supporting all the components defined in the specification',
149    'dmpf' => 'Digital Media Project', # various
150    'drc1' => 'Dirac (wavelet compression), encapsulated in ISO base media (MP4)',
151    'dv1a' => 'DMB MAF vid w/ AVC vid, ER-BSAC aud, BIFS, JPG/PNG/MNG images, TS',
152    'dv1b' => 'DMB MAF, extending dv1a, with 3GPP timed text, DID, TVA, REL, IPMP',
153    'dv2a' => 'DMB MAF vid w/ AVC vid, HE-AAC v2 aud, BIFS, JPG/PNG/MNG images, TS',
154    'dv2b' => 'DMB MAF, extending dv2a, with 3GPP timed text, DID, TVA, REL, IPMP',
155    'dv3a' => 'DMB MAF vid w/ AVC vid, HE-AAC aud, BIFS, JPG/PNG/MNG images, TS',
156    'dv3b' => 'DMB MAF, extending dv3a, with 3GPP timed text, DID, TVA, REL, IPMP',
157    'dvr1' => 'DVB (.DVB) over RTP', # video/vnd.dvb.file
158    'dvt1' => 'DVB (.DVB) over MPEG-2 Transport Stream', # video/vnd.dvb.file
159    'F4A ' => 'Audio for Adobe Flash Player 9+ (.F4A)', # audio/mp4
160    'F4B ' => 'Audio Book for Adobe Flash Player 9+ (.F4B)', # audio/mp4
161    'F4P ' => 'Protected Video for Adobe Flash Player 9+ (.F4P)', # video/mp4
162    'F4V ' => 'Video for Adobe Flash Player 9+ (.F4V)', # video/mp4
163    'isc2' => 'ISMACryp 2.0 Encrypted File', # ?/enc-isoff-generic
164    'iso2' => 'MP4 Base Media v2 [ISO 14496-12:2005]', # video/mp4 (or audio)
165    'iso3' => 'MP4 Base Media v3', # video/mp4 (or audio)
166    'iso4' => 'MP4 Base Media v4', # video/mp4 (or audio)
167    'iso5' => 'MP4 Base Media v5', # video/mp4 (or audio)
168    'iso6' => 'MP4 Base Media v6', # video/mp4 (or audio)
169    'iso7' => 'MP4 Base Media v7', # video/mp4 (or audio)
170    'iso8' => 'MP4 Base Media v8', # video/mp4 (or audio)
171    'iso9' => 'MP4 Base Media v9', # video/mp4 (or audio)
172    'isom' => 'MP4 Base Media v1 [IS0 14496-12:2003]', # video/mp4 (or audio)
173    'JP2 ' => 'JPEG 2000 Image (.JP2) [ISO 15444-1 ?]', # image/jp2
174    'JP20' => 'Unknown, from GPAC samples (prob non-existent)',
175    'jpm ' => 'JPEG 2000 Compound Image (.JPM) [ISO 15444-6]', # image/jpm
176    'jpx ' => 'JPEG 2000 with extensions (.JPX) [ISO 15444-2]', # image/jpx
177    'KDDI' => '3GPP2 EZmovie for KDDI 3G cellphones', # video/3gpp2
178    #LCAG  => (found in CompatibleBrands of Leica MOV videos)
179    'M4A ' => 'Apple iTunes AAC-LC (.M4A) Audio', # audio/x-m4a
180    'M4B ' => 'Apple iTunes AAC-LC (.M4B) Audio Book', # audio/mp4
181    'M4P ' => 'Apple iTunes AAC-LC (.M4P) AES Protected Audio', # audio/mp4
182    'M4V ' => 'Apple iTunes Video (.M4V) Video', # video/x-m4v
183    'M4VH' => 'Apple TV (.M4V)', # video/x-m4v
184    'M4VP' => 'Apple iPhone (.M4V)', # video/x-m4v
185    'mj2s' => 'Motion JPEG 2000 [ISO 15444-3] Simple Profile', # video/mj2
186    'mjp2' => 'Motion JPEG 2000 [ISO 15444-3] General Profile', # video/mj2
187    'mmp4' => 'MPEG-4/3GPP Mobile Profile (.MP4/3GP) (for NTT)', # video/mp4
188    'mp21' => 'MPEG-21 [ISO/IEC 21000-9]', # various
189    'mp41' => 'MP4 v1 [ISO 14496-1:ch13]', # video/mp4
190    'mp42' => 'MP4 v2 [ISO 14496-14]', # video/mp4
191    'mp71' => 'MP4 w/ MPEG-7 Metadata [per ISO 14496-12]', # various
192    'MPPI' => 'Photo Player, MAF [ISO/IEC 23000-3]', # various
193    'mqt ' => 'Sony / Mobile QuickTime (.MQV) US Patent 7,477,830 (Sony Corp)', # video/quicktime
194    'MSNV' => 'MPEG-4 (.MP4) for SonyPSP', # audio/mp4
195    'NDAS' => 'MP4 v2 [ISO 14496-14] Nero Digital AAC Audio', # audio/mp4
196    'NDSC' => 'MPEG-4 (.MP4) Nero Cinema Profile', # video/mp4
197    'NDSH' => 'MPEG-4 (.MP4) Nero HDTV Profile', # video/mp4
198    'NDSM' => 'MPEG-4 (.MP4) Nero Mobile Profile', # video/mp4
199    'NDSP' => 'MPEG-4 (.MP4) Nero Portable Profile', # video/mp4
200    'NDSS' => 'MPEG-4 (.MP4) Nero Standard Profile', # video/mp4
201    'NDXC' => 'H.264/MPEG-4 AVC (.MP4) Nero Cinema Profile', # video/mp4
202    'NDXH' => 'H.264/MPEG-4 AVC (.MP4) Nero HDTV Profile', # video/mp4
203    'NDXM' => 'H.264/MPEG-4 AVC (.MP4) Nero Mobile Profile', # video/mp4
204    'NDXP' => 'H.264/MPEG-4 AVC (.MP4) Nero Portable Profile', # video/mp4
205    'NDXS' => 'H.264/MPEG-4 AVC (.MP4) Nero Standard Profile', # video/mp4
206    'odcf' => 'OMA DCF DRM Format 2.0 (OMA-TS-DRM-DCF-V2_0-20060303-A)', # various
207    'opf2' => 'OMA PDCF DRM Format 2.1 (OMA-TS-DRM-DCF-V2_1-20070724-C)',
208    'opx2' => 'OMA PDCF DRM + XBS extensions (OMA-TS-DRM_XBS-V1_0-20070529-C)',
209    'pana' => 'Panasonic Digital Camera',
210    'qt  ' => 'Apple QuickTime (.MOV/QT)', # video/quicktime
211    'ROSS' => 'Ross Video',
212    'sdv ' => 'SD Memory Card Video', # various?
213    'ssc1' => 'Samsung stereoscopic, single stream',
214    'ssc2' => 'Samsung stereoscopic, dual stream',
215    'XAVC' => 'Sony XAVC', #PH
216    'heic' => 'High Efficiency Image Format HEVC still image (.HEIC)', # image/heic
217    'hevc' => 'High Efficiency Image Format HEVC sequence (.HEICS)', # image/heic-sequence
218    'mif1' => 'High Efficiency Image Format still image (.HEIF)', # image/heif
219    'msf1' => 'High Efficiency Image Format sequence (.HEIFS)', # image/heif-sequence
220    'heix' => 'High Efficiency Image Format still image (.HEIF)', # image/heif (ref PH, Canon 1DXmkIII)
221    'avif' => 'AV1 Image File Format (.AVIF)', # image/avif
222    'crx ' => 'Canon Raw (.CRX)', #PH (CR3 or CRM; use Canon CompressorVersion to decide)
223);
224
225# information for int32u date/time tags (time zero is Jan 1, 1904)
226my %timeInfo = (
227    Notes => 'converted from UTC to local time if the QuickTimeUTC option is set',
228    Shift => 'Time',
229    Writable => 1,
230    Permanent => 1,
231    DelValue => 0,
232    # It is not uncommon for brain-dead software to use the wrong time zero, it should be
233    # Jan 1, 1904, so assume a time zero of Jan 1, 1970 if the date is before this
234    # Note: This value will be in UTC if generated by a system that is aware of the time zone
235    # (also note: this code is duplicated for the CreateDate tag)
236    RawConv => q{
237        my $offset = (66 * 365 + 17) * 24 * 3600;
238        return $val - $offset if $val >= $offset or $$self{OPTIONS}{QuickTimeUTC};
239        if ($val and not $$self{IsWriting}) {
240            $self->WarnOnce('Patched incorrect time zero for QuickTime date/time tag',1);
241        }
242        return $val;
243    },
244    RawConvInv => q{
245        if ($$self{FileType} eq 'CR3' and not $self->Options('QuickTimeUTC')) {
246            # convert to UTC
247            my $offset = (66 * 365 + 17) * 24 * 3600;
248            $val = ConvertUnixTime($val - $offset);
249            $val = GetUnixTime($val, 1) + $offset;
250        }
251        return $val;
252    },
253    # (all CR3 files store UTC times - PH)
254    ValueConv => 'ConvertUnixTime($val, $self->Options("QuickTimeUTC") || $$self{FileType} eq "CR3")',
255    ValueConvInv => q{
256        $val = GetUnixTime($val, $self->Options("QuickTimeUTC"));
257        return undef unless defined $val;
258        return $val + (66 * 365 + 17) * 24 * 3600;
259    },
260    PrintConv => '$self->ConvertDateTime($val)',
261    PrintConvInv => '$self->InverseDateTime($val)',
262    # (can't put Groups here because they aren't constant!)
263);
264# information for duration tags
265my %durationInfo = (
266    ValueConv => '$$self{TimeScale} ? $val / $$self{TimeScale} : $val',
267    PrintConv => '$$self{TimeScale} ? ConvertDuration($val) : $val',
268);
269# handle unknown tags
270my %unknownInfo = (
271    Unknown => 1,
272    ValueConv => '$val =~ /^([\x20-\x7e]*)\0*$/ ? $1 : \$val',
273);
274
275# multi-language text with 6-byte header
276my %langText = ( IText => 6 );
277
278# parsing for most of the 3gp udta language text boxes
279my %langText3gp = (
280    Notes => 'used in 3gp videos',
281    Avoid => 1,
282    IText => 6,
283);
284
285# 4-character Vendor ID codes (ref PH)
286my %vendorID = (
287    appl => 'Apple',
288    fe20 => 'Olympus (fe20)', # (FE200)
289    FFMP => 'FFmpeg',
290   'GIC '=> 'General Imaging Co.',
291    kdak => 'Kodak',
292    KMPI => 'Konica-Minolta',
293    leic => 'Leica',
294    mino => 'Minolta',
295    niko => 'Nikon',
296    NIKO => 'Nikon',
297    olym => 'Olympus',
298    pana => 'Panasonic',
299    pent => 'Pentax',
300    pr01 => 'Olympus (pr01)', # (FE100,FE110,FE115)
301    sany => 'Sanyo',
302   'SMI '=> 'Sorenson Media Inc.',
303    ZORA => 'Zoran Corporation',
304   'AR.D'=> 'Parrot AR.Drone',
305   ' KD '=> 'Kodak', # (FZ201)
306);
307
308# QuickTime data atom encodings for string types (ref 12)
309%stringEncoding = (
310    1 => 'UTF8',
311    2 => 'UTF16',
312    3 => 'ShiftJIS',
313    4 => 'UTF8',
314    5 => 'UTF16',
315);
316
317my %graphicsMode = (
318    # (ref http://homepage.mac.com/vanhoek/MovieGuts%20docs/64.html)
319    0x00 => 'srcCopy',
320    0x01 => 'srcOr',
321    0x02 => 'srcXor',
322    0x03 => 'srcBic',
323    0x04 => 'notSrcCopy',
324    0x05 => 'notSrcOr',
325    0x06 => 'notSrcXor',
326    0x07 => 'notSrcBic',
327    0x08 => 'patCopy',
328    0x09 => 'patOr',
329    0x0a => 'patXor',
330    0x0b => 'patBic',
331    0x0c => 'notPatCopy',
332    0x0d => 'notPatOr',
333    0x0e => 'notPatXor',
334    0x0f => 'notPatBic',
335    0x20 => 'blend',
336    0x21 => 'addPin',
337    0x22 => 'addOver',
338    0x23 => 'subPin',
339    0x24 => 'transparent',
340    0x25 => 'addMax',
341    0x26 => 'subOver',
342    0x27 => 'addMin',
343    0x31 => 'grayishTextOr',
344    0x32 => 'hilite',
345    0x40 => 'ditherCopy',
346    # the following ref ISO/IEC 15444-3
347    0x100 => 'Alpha',
348    0x101 => 'White Alpha',
349    0x102 => 'Pre-multiplied Black Alpha',
350    0x110 => 'Component Alpha',
351);
352
353my %channelLabel = (
354    0xFFFFFFFF => 'Unknown',
355    0 => 'Unused',
356    100 => 'UseCoordinates',
357    1 => 'Left',
358    2 => 'Right',
359    3 => 'Center',
360    4 => 'LFEScreen',
361    5 => 'LeftSurround',
362    6 => 'RightSurround',
363    7 => 'LeftCenter',
364    8 => 'RightCenter',
365    9 => 'CenterSurround',
366    10 => 'LeftSurroundDirect',
367    11 => 'RightSurroundDirect',
368    12 => 'TopCenterSurround',
369    13 => 'VerticalHeightLeft',
370    14 => 'VerticalHeightCenter',
371    15 => 'VerticalHeightRight',
372    16 => 'TopBackLeft',
373    17 => 'TopBackCenter',
374    18 => 'TopBackRight',
375    33 => 'RearSurroundLeft',
376    34 => 'RearSurroundRight',
377    35 => 'LeftWide',
378    36 => 'RightWide',
379    37 => 'LFE2',
380    38 => 'LeftTotal',
381    39 => 'RightTotal',
382    40 => 'HearingImpaired',
383    41 => 'Narration',
384    42 => 'Mono',
385    43 => 'DialogCentricMix',
386    44 => 'CenterSurroundDirect',
387    45 => 'Haptic',
388    200 => 'Ambisonic_W',
389    201 => 'Ambisonic_X',
390    202 => 'Ambisonic_Y',
391    203 => 'Ambisonic_Z',
392    204 => 'MS_Mid',
393    205 => 'MS_Side',
394    206 => 'XY_X',
395    207 => 'XY_Y',
396    301 => 'HeadphonesLeft',
397    302 => 'HeadphonesRight',
398    304 => 'ClickTrack',
399    305 => 'ForeignLanguage',
400    400 => 'Discrete',
401    0x10000 => 'Discrete_0',
402    0x10001 => 'Discrete_1',
403    0x10002 => 'Discrete_2',
404    0x10003 => 'Discrete_3',
405    0x10004 => 'Discrete_4',
406    0x10005 => 'Discrete_5',
407    0x10006 => 'Discrete_6',
408    0x10007 => 'Discrete_7',
409    0x10008 => 'Discrete_8',
410    0x10009 => 'Discrete_9',
411    0x1000a => 'Discrete_10',
412    0x1000b => 'Discrete_11',
413    0x1000c => 'Discrete_12',
414    0x1000d => 'Discrete_13',
415    0x1000e => 'Discrete_14',
416    0x1000f => 'Discrete_15',
417    0x1ffff => 'Discrete_65535',
418);
419
420# properties which don't get inherited from the parent
421my %dontInherit = (
422    ispe => 1,  # size of parent may be different
423    hvcC => 1,  # (likely redundant)
424);
425
426# tags that may be duplicated and directories that may contain duplicate tags
427# (used only to avoid warnings when Validate-ing)
428my %dupTagOK = ( mdat => 1, trak => 1, free => 1, infe => 1, sgpd => 1, dimg => 1, CCDT => 1,
429                 sbgp => 1, csgm => 1, uuid => 1, cdsc => 1, maxr => 1, '----' => 1 );
430my %dupDirOK = ( ipco => 1, '----' => 1 );
431
432# the usual atoms required to decode timed metadata with the ExtractEmbedded option
433my %eeStd = ( stco => 'stbl', co64 => 'stbl', stsz => 'stbl', stz2 => 'stbl',
434              stsc => 'stbl', stts => 'stbl' );
435
436# boxes and their containers for the various handler types that we want to save
437# when the ExtractEmbedded is enabled (currently only the 'gps ' container name is
438# used, but others have been checked against all available sample files and may be
439# useful in the future if the names are used for different boxes on other locations)
440my %eeBox = (
441    # (note: vide is only processed if specific atoms exist in the VideoSampleDesc)
442    vide => { %eeStd,
443        JPEG => 'stsd',
444    },
445    text => { %eeStd },
446    meta => { %eeStd },
447    sbtl => { %eeStd },
448    data => { %eeStd },
449    camm => { %eeStd }, # (Insta360)
450    ctbx => { %eeStd }, # (GM cars)
451    ''   => { 'gps ' => 'moov', 'GPS ' => 'main' }, # (no handler -- in top level 'moov' box, and main)
452);
453# boxes to save when ExtractEmbedded is set to 2 or higher
454my %eeBox2 = (
455    vide => { avcC => 'stsd' }, # (parses H264 video stream)
456);
457
458# QuickTime atoms
459%Image::ExifTool::QuickTime::Main = (
460    PROCESS_PROC => \&ProcessMOV,
461    WRITE_PROC => \&WriteQuickTime, # (only needs to be defined for directories to process when writing)
462    GROUPS => { 2 => 'Video' },
463    meta => { # 'meta' is found here in my Sony ILCE-7S MP4 sample - PH
464        Name => 'Meta',
465        SubDirectory => {
466            TagTable => 'Image::ExifTool::QuickTime::Meta',
467            Start => 4, # skip 4-byte version number header
468        },
469    },
470    meco => { #ISO14496-12:2015
471        Name => 'OtherMeta',
472        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::OtherMeta' },
473    },
474    free => [
475        {
476            Name => 'KodakFree',
477            # (found in Kodak M5370 MP4 videos)
478            Condition => '$$valPt =~ /^\0\0\0.Seri/s',
479            SubDirectory => { TagTable => 'Image::ExifTool::Kodak::Free' },
480        },{
481            Name => 'Pittasoft',
482            # (Pittasoft Blackview dashcam MP4 videos)
483            Condition => '$$valPt =~ /^\0\0..(cprt|sttm|ptnm|ptrh|thum|gps |3gf )/s',
484            SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Pittasoft' },
485        },{
486            Unknown => 1,
487            Binary => 1,
488        },
489        # (also Samsung WB750 uncompressed thumbnail data starting with "SDIC\0")
490    ],
491    # fre1 - 4 bytes: "june" (Kodak PixPro SP360)
492    frea => {
493        Name => 'Kodak_frea',
494        SubDirectory => { TagTable => 'Image::ExifTool::Kodak::frea' },
495    },
496    skip => [
497        {
498            Name => 'CanonSkip',
499            Condition => '$$valPt =~ /^\0.{3}(CNDB|CNCV|CNMN|CNFV|CNTH|CNDM)/s',
500            SubDirectory => { TagTable => 'Image::ExifTool::Canon::Skip' },
501        },
502        {
503            Name => 'PreviewImage', # (found in  DuDuBell M1 dashcam MOV files)
504            Groups => { 2 => 'Preview' },
505            Condition => '$$valPt =~ /^.{12}\xff\xd8\xff/',
506            Binary => 1,
507            RawConv => q{
508                my $len = Get32u(\$val, 8);
509                return undef unless length($val) >= $len + 12;
510                return substr($val, 12, $len);
511            },
512        },
513        { Name => 'Skip', Unknown => 1, Binary => 1 },
514    ],
515    wide => { Unknown => 1, Binary => 1 },
516    ftyp => { #MP4
517        Name => 'FileType',
518        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::FileType' },
519    },
520    pnot => {
521        Name => 'Preview',
522        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Preview' },
523    },
524    PICT => {
525        Name => 'PreviewPICT',
526        Groups => { 2 => 'Preview' },
527        Binary => 1,
528    },
529    pict => { #8
530        Name => 'PreviewPICT',
531        Groups => { 2 => 'Preview' },
532        Binary => 1,
533    },
534    # (note that moov is present for an HEIF sequence)
535    moov => {
536        Name => 'Movie',
537        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Movie' },
538    },
539    moof => {
540        Name => 'MovieFragment',
541        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MovieFragment' },
542    },
543    # mfra - movie fragment random access: contains tfra (track fragment random access), and
544    #           mfro (movie fragment random access offset) (ref 5)
545    mdat => { Name => 'MediaData', Unknown => 1, Binary => 1 },
546    'mdat-size' => {
547        Name => 'MediaDataSize',
548        Notes => q{
549            not a real tag ID, this tag represents the size of the 'mdat' data in bytes
550            and is used in the AvgBitrate calculation
551        },
552    },
553    'mdat-offset' => 'MediaDataOffset',
554    junk => { Unknown => 1, Binary => 1 }, #8
555    uuid => [
556        { #9 (MP4 files)
557            Name => 'XMP',
558            # *** this is where ExifTool writes XMP in MP4 videos (as per XMP spec) ***
559            Condition => '$$valPt=~/^\xbe\x7a\xcf\xcb\x97\xa9\x42\xe8\x9c\x71\x99\x94\x91\xe3\xaf\xac/',
560            WriteGroup => 'XMP',    # (write main XMP tags here)
561            SubDirectory => {
562                TagTable => 'Image::ExifTool::XMP::Main',
563                Start => 16,
564            },
565        },
566        { #11 (MP4 files)
567            Name => 'UUID-PROF',
568            Condition => '$$valPt=~/^PROF!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
569            SubDirectory => {
570                TagTable => 'Image::ExifTool::QuickTime::Profile',
571                Start => 24, # uid(16) + version(1) + flags(3) + count(4)
572            },
573        },
574        { #PH (Flip MP4 files)
575            Name => 'UUID-Flip',
576            Condition => '$$valPt=~/^\x4a\xb0\x3b\x0f\x61\x8d\x40\x75\x82\xb2\xd9\xfa\xce\xd3\x5f\xf5/',
577            SubDirectory => {
578                TagTable => 'Image::ExifTool::QuickTime::Flip',
579                Start => 16,
580            },
581        },
582        # "\x98\x7f\xa3\xdf\x2a\x85\x43\xc0\x8f\x8f\xd9\x7c\x47\x1e\x8e\xea" - unknown data in Flip videos
583        { #PH (Canon CR3)
584            Name => 'UUID-Canon2',
585            WriteLast => 1, # MUST come after mdat or DPP will drop mdat when writing!
586            Condition => '$$valPt=~/^\x21\x0f\x16\x87\x91\x49\x11\xe4\x81\x11\x00\x24\x21\x31\xfc\xe4/',
587            SubDirectory => {
588                TagTable => 'Image::ExifTool::Canon::uuid2',
589                Start => 16,
590            },
591        },
592        { # (ref https://github.com/JamesHeinrich/getID3/blob/master/getid3/module.audio-video.quicktime.php)
593            Name => 'SensorData', # sensor data for the 360Fly
594            Condition => '$$valPt=~/^\xef\xe1\x58\x9a\xbb\x77\x49\xef\x80\x95\x27\x75\x9e\xb1\xdc\x6f/ and $$self{OPTIONS}{ExtractEmbedded}',
595            SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Tags360Fly' },
596        },
597        {
598            Name => 'SensorData',
599            Condition => '$$valPt=~/^\xef\xe1\x58\x9a\xbb\x77\x49\xef\x80\x95\x27\x75\x9e\xb1\xdc\x6f/',
600            Notes => 'raw 360Fly sensor data without ExtractEmbedded option',
601            RawConv => q{
602                $self->WarnOnce('Use the ExtractEmbedded option to decode timed SensorData',3);
603                return \$val;
604            },
605        },
606        { #PH (Canon CR3)
607            Name => 'PreviewImage',
608            Condition => '$$valPt=~/^\xea\xf4\x2b\x5e\x1c\x98\x4b\x88\xb9\xfb\xb7\xdc\x40\x6e\x4d\x16/',
609            Groups => { 2 => 'Preview' },
610            # 0x00 - undef[16]: UUID
611            # 0x10 - int32u[2]: "0 1" (version and/or item count?)
612            # 0x18 - int32u: PRVW atom size
613            # 0x20 - int32u: 'PRVW'
614            # 0x30 - int32u: 0
615            # 0x34 - int16u: 1
616            # 0x36 - int16u: image width
617            # 0x38 - int16u: image height
618            # 0x3a - int16u: 1
619            # 0x3c - int32u: preview length
620            RawConv => '$val = substr($val, 0x30); $self->ValidateImage(\$val, $tag)',
621        },
622        { #8
623            Name => 'UUID-Unknown',
624            %unknownInfo,
625        },
626    ],
627    _htc => {
628        Name => 'HTCInfo',
629        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HTCInfo' },
630    },
631    udta => {
632        Name => 'UserData',
633        SubDirectory => { TagTable => 'Image::ExifTool::FLIR::UserData' },
634    },
635    thum => { #PH
636        Name => 'ThumbnailImage',
637        Groups => { 2 => 'Preview' },
638        Binary => 1,
639    },
640   'thm ' => { #PH (70mai A800)
641        Name => 'ThumbnailImage',
642        Groups => { 2 => 'Preview' },
643        Binary => 1,
644    },
645    ardt => { #PH
646        Name => 'ARDroneFile',
647        ValueConv => 'length($val) > 4 ? substr($val,4) : $val', # remove length
648    },
649    prrt => { #PH
650        Name => 'ARDroneTelemetry',
651        Notes => q{
652            telemetry information for each video frame: status1, status2, time, pitch,
653            roll, yaw, speed, altitude
654        },
655        ValueConv => q{
656            my $size = length $val;
657            return \$val if $size < 12 or not $$self{OPTIONS}{Binary};
658            my $len = Get16u(\$val, 2);
659            my $str = '';
660            SetByteOrder('II');
661            my $pos = 12;
662            while ($pos + $len <= $size) {
663                my $s1 = Get16u(\$val, $pos);
664                # s2: 7=take-off?, 3=moving, 4=hovering, 9=landing?, 2=landed
665                my $s2 = Get16u(\$val, $pos + 2);
666                $str .= "$s1 $s2";
667                my $num = int(($len-4)/4);
668                my ($i, $v);
669                for ($i=0; $i<$num; ++$i) {
670                    my $pt = $pos + 4 + $i * 4;
671                    if ($i > 0 && $i < 4) {
672                        $v = GetFloat(\$val, $pt); # pitch/roll/yaw
673                    } else {
674                        $v = Get32u(\$val, $pt);
675                        # convert time to sec, and speed(NC)/altitude to metres
676                        $v /= 1000 if $i <= 5;
677                    }
678                    $str .= " $v";
679                }
680                $str .= "\n";
681                $pos += $len;
682            }
683            SetByteOrder('MM');
684            return \$str;
685        },
686    },
687    udat => { #PH (GPS NMEA-format log written by Datakam Player software)
688        Name => 'GPSLog',
689        Binary => 1,    # (actually ASCII, but very lengthy)
690        Notes => 'parsed to extract GPS separately when ExtractEmbedded is used',
691        RawConv => q{
692            $val =~ s/\0+$//;   # remove trailing nulls
693            if (length $val and $$self{OPTIONS}{ExtractEmbedded}) {
694                my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
695                Image::ExifTool::QuickTime::ProcessGPSLog($self, { DataPt => \$val }, $tagTbl);
696            }
697            return $val;
698        },
699    },
700    # meta - proprietary XML information written by some Flip cameras - PH
701    # beam - 16 bytes found in an iPhone video
702    IDIT => { #PH (written by DuDuBell M1, VSYS M6L dashcams)
703        Name => 'DateTimeOriginal',
704        Description => 'Date/Time Original',
705        Groups => { 2 => 'Time' },
706        Format => 'string', # (removes trailing "\0")
707        Shift => 'Time',
708        Writable => 1,
709        Permanent => 1,
710        DelValue => '0000-00-00T00:00:00+0000',
711        ValueConv => '$val=~tr/-/:/; $val',
712        ValueConvInv => '$val=~s/(\d+):(\d+):/$1-$2-/; $val',
713        PrintConv => '$self->ConvertDateTime($val)',
714        PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
715    },
716    gps0 => { #PH (DuDuBell M1, VSYS M6L)
717        Name => 'GPSTrack',
718        SubDirectory => {
719            TagTable => 'Image::ExifTool::QuickTime::Stream',
720            ProcessProc => \&Process_gps0,
721        },
722    },
723    gsen => { #PH (DuDuBell M1, VSYS M6L)
724        Name => 'GSensor',
725        SubDirectory => {
726            TagTable => 'Image::ExifTool::QuickTime::Stream',
727            ProcessProc => \&Process_gsen,
728        },
729    },
730    # gpsa - seen hex "01 20 00 00" (DuDuBell M1, VSYS M6L)
731    # gsea - 20 bytes hex "05 00's..." (DuDuBell M1) "05 08 02 01 ..." (VSYS M6L)
732   'GPS ' => {  # GPS data written by 70mai dashcam (parsed in QuickTimeStream.pl)
733        Name => 'GPSDataList2',
734        Unknown => 1,
735        Binary => 1,
736    },
737    sefd => {
738        Name => 'SamsungTrailer',
739        SubDirectory => { TagTable => 'Image::ExifTool::Samsung::Trailer' },
740    },
741);
742
743# MPEG-4 'ftyp' atom
744# (ref http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html)
745%Image::ExifTool::QuickTime::FileType = (
746    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
747    GROUPS => { 2 => 'Video' },
748    FORMAT => 'int32u',
749    0 => {
750        Name => 'MajorBrand',
751        Format => 'undef[4]',
752        PrintConv => \%ftypLookup,
753    },
754    1 => {
755        Name => 'MinorVersion',
756        Format => 'undef[4]',
757        ValueConv => 'sprintf("%x.%x.%x", unpack("nCC", $val))',
758    },
759    2 => {
760        Name => 'CompatibleBrands',
761        Format => 'undef[$size-8]',
762        # ignore any entry with a null, and return others as a list
763        ValueConv => 'my @a=($val=~/.{4}/sg); @a=grep(!/\0/,@a); \@a',
764    },
765);
766
767# proprietary HTC atom (HTC One MP4 video)
768%Image::ExifTool::QuickTime::HTCInfo = (
769    PROCESS_PROC => \&ProcessMOV,
770    GROUPS => { 2 => 'Video' },
771    NOTES => 'Tags written by some HTC camera phones.',
772    slmt => {
773        Name => 'Unknown_slmt',
774        Unknown => 1,
775        Format => 'int32u', # (observed values: 4)
776    },
777);
778
779# atoms used in QTIF files
780%Image::ExifTool::QuickTime::ImageFile = (
781    PROCESS_PROC => \&ProcessMOV,
782    GROUPS => { 2 => 'Image' },
783    NOTES => 'Tags used in QTIF QuickTime Image Files.',
784    idsc => {
785        Name => 'ImageDescription',
786        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ImageDesc' },
787    },
788    idat => {
789        Name => 'ImageData',
790        Binary => 1,
791    },
792    iicc => {
793        Name => 'ICC_Profile',
794        SubDirectory => { TagTable => 'Image::ExifTool::ICC_Profile::Main' },
795    },
796);
797
798# image description data block
799%Image::ExifTool::QuickTime::ImageDesc = (
800    PROCESS_PROC => \&ProcessHybrid,
801    VARS => { ID_LABEL => 'ID/Index' },
802    GROUPS => { 2 => 'Image' },
803    FORMAT => 'int16u',
804    2 => {
805        Name => 'CompressorID',
806        Format => 'string[4]',
807# not very useful since this isn't a complete list and name is given below
808#        # ref http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html
809#        PrintConv => {
810#            cvid => 'Cinepak',
811#            jpeg => 'JPEG',
812#           'smc '=> 'Graphics',
813#           'rle '=> 'Animation',
814#            rpza => 'Apple Video',
815#            kpcd => 'Kodak Photo CD',
816#           'png '=> 'Portable Network Graphics',
817#            mjpa => 'Motion-JPEG (format A)',
818#            mjpb => 'Motion-JPEG (format B)',
819#            SVQ1 => 'Sorenson video, version 1',
820#            SVQ3 => 'Sorenson video, version 3',
821#            mp4v => 'MPEG-4 video',
822#           'dvc '=> 'NTSC DV-25 video',
823#            dvcp => 'PAL DV-25 video',
824#           'gif '=> 'Compuserve Graphics Interchange Format',
825#            h263 => 'H.263 video',
826#            tiff => 'Tagged Image File Format',
827#           'raw '=> 'Uncompressed RGB',
828#           '2vuY'=> "Uncompressed Y'CbCr, 3x8-bit 4:2:2 (2vuY)",
829#           'yuv2'=> "Uncompressed Y'CbCr, 3x8-bit 4:2:2 (yuv2)",
830#            v308 => "Uncompressed Y'CbCr, 8-bit 4:4:4",
831#            v408 => "Uncompressed Y'CbCr, 8-bit 4:4:4:4",
832#            v216 => "Uncompressed Y'CbCr, 10, 12, 14, or 16-bit 4:2:2",
833#            v410 => "Uncompressed Y'CbCr, 10-bit 4:4:4",
834#            v210 => "Uncompressed Y'CbCr, 10-bit 4:2:2",
835#            hvc1 => 'HEVC', #PH
836#        },
837    },
838    10 => {
839        Name => 'VendorID',
840        Format => 'string[4]',
841        RawConv => 'length $val ? $val : undef',
842        PrintConv => \%vendorID,
843        SeparateTable => 'VendorID',
844    },
845  # 14 - ("Quality" in QuickTime docs) ??
846    16 => 'SourceImageWidth',
847    17 => 'SourceImageHeight',
848    18 => { Name => 'XResolution',  Format => 'fixed32u' },
849    20 => { Name => 'YResolution',  Format => 'fixed32u' },
850  # 24 => 'FrameCount', # always 1 (what good is this?)
851    25 => {
852        Name => 'CompressorName',
853        Format => 'string[32]',
854        # (sometimes this is a Pascal string, and sometimes it is a C string)
855        RawConv => q{
856            $val=substr($val,1,ord($1)) if $val=~/^([\0-\x1f])/ and ord($1)<length($val);
857            length $val ? $val : undef;
858        },
859    },
860    41 => 'BitDepth',
861#
862# Observed offsets for child atoms of various CompressorID types:
863#
864#   CompressorID  Offset  Child atoms
865#   -----------   ------  ----------------
866#   avc1          86      avcC, btrt, colr, pasp, fiel, clap, svcC
867#   mp4v          86      esds, pasp
868#   s263          86      d263
869#
870    btrt => {
871        Name => 'BitrateInfo',
872        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Bitrate' },
873    },
874    # Reference for fiel, colr, pasp, clap:
875    # https://developer.apple.com/library/mac/technotes/tn2162/_index.html#//apple_ref/doc/uid/DTS40013070-CH1-TNTAG9
876    fiel => {
877        Name => 'VideoFieldOrder',
878        ValueConv => 'join(" ", unpack("C*",$val))',
879        PrintConv => [{
880            1 => 'Progressive',
881            2 => '2:1 Interlaced',
882        }],
883    },
884    colr => {
885        Name => 'ColorRepresentation',
886        ValueConv => 'join(" ", substr($val,0,4), unpack("x4n*",$val))',
887    },
888    pasp => {
889        Name => 'PixelAspectRatio',
890        ValueConv => 'join(":", unpack("N*",$val))',
891    },
892    clap => {
893        Name => 'CleanAperture',
894        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::CleanAperture' },
895    },
896    avcC => {
897        # (see http://thompsonng.blogspot.ca/2010/11/mp4-file-format-part-2.html)
898        Name => 'AVCConfiguration',
899        Unknown => 1,
900        Binary => 1,
901    },
902    JPEG => { # (found in CR3 images; used as a flag to identify JpgFromRaw 'vide' stream)
903        Name => 'JPEGInfo',
904        # (4 bytes all zero)
905        Unknown => 1,
906        Binary => 1,
907    },
908    # hvcC - HEVC configuration
909    # svcC - 7 bytes: 00 00 00 00 ff e0 00
910    # esds - elementary stream descriptor
911    # d263
912    gama => { Name => 'Gamma', Format => 'fixed32u' },
913    # mjqt - default quantization table for MJPEG
914    # mjht - default Huffman table for MJPEG
915    # csgm ? (seen in hevc video)
916    CMP1 => { # Canon CR3
917        Name => 'CMP1',
918        SubDirectory => { TagTable => 'Image::ExifTool::Canon::CMP1' },
919    },
920    CDI1 => { # Canon CR3
921        Name => 'CDI1',
922        SubDirectory => {
923            TagTable => 'Image::ExifTool::Canon::CDI1',
924            Start => 4,
925        },
926    },
927    # JPEG - 4 bytes all 0 (Canon CR3)
928    # free - (Canon CR3)
929#
930# spherical video v2 stuff (untested)
931#
932    st3d => {
933        Name => 'Stereoscopic3D',
934        Format => 'int8u',
935        ValueConv => '$val =~ s/.* //; $val', # (remove leading version/flags bytes?)
936        PrintConv => {
937            0 => 'Monoscopic',
938            1 => 'Stereoscopic Top-Bottom',
939            2 => 'Stereoscopic Left-Right',
940            3 => 'Stereoscopic Stereo-Custom', # (provisional in spec as of 2017-10-10)
941        },
942    },
943    sv3d => {
944        Name => 'SphericalVideo',
945        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::sv3d' },
946    },
947);
948
949# 'sv3d' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
950%Image::ExifTool::QuickTime::sv3d = (
951    PROCESS_PROC => \&ProcessMOV,
952    GROUPS => { 2 => 'Video' },
953    NOTES => q{
954        Tags defined by the Spherical Video V2 specification.  See
955        L<https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md>
956        for the specification.
957    },
958    svhd => {
959        Name => 'MetadataSource',
960        Format => 'undef',
961        ValueConv => '$val=~tr/\0//d; $val', # (remove version/flags? and terminator?)
962    },
963    proj => {
964        Name => 'Projection',
965        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::proj' },
966    },
967);
968
969# 'proj' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
970%Image::ExifTool::QuickTime::proj = (
971    PROCESS_PROC => \&ProcessMOV,
972    GROUPS => { 2 => 'Video' },
973    prhd => {
974        Name => 'ProjectionHeader',
975        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::prhd' },
976    },
977    cbmp => {
978        Name => 'CubemapProj',
979        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::cbmp' },
980    },
981    equi => {
982        Name => 'EquirectangularProj',
983        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::equi' },
984    },
985    # mshp - MeshProjection (P.I.T.A. to decode, for not much reward, see ref)
986);
987
988# 'prhd' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
989%Image::ExifTool::QuickTime::prhd = (
990    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
991    GROUPS => { 2 => 'Video' },
992    FORMAT => 'fixed32s',
993    # 0 - version (high 8 bits) / flags (low 24 bits)
994    1 => 'PoseYawDegrees',
995    2 => 'PosePitchDegrees',
996    3 => 'PoseRollDegrees',
997);
998
999# 'cbmp' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1000%Image::ExifTool::QuickTime::cbmp = (
1001    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1002    GROUPS => { 2 => 'Video' },
1003    FORMAT => 'int32u',
1004    # 0 - version (high 8 bits) / flags (low 24 bits)
1005    1 => 'Layout',
1006    2 => 'Padding',
1007);
1008
1009# 'equi' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1010%Image::ExifTool::QuickTime::equi = (
1011    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1012    GROUPS => { 2 => 'Video' },
1013    FORMAT => 'int32u', # (actually 0.32 fixed point)
1014    # 0 - version (high 8 bits) / flags (low 24 bits)
1015    1 => { Name => 'ProjectionBoundsTop',   ValueConv => '$val / 4294967296' },
1016    2 => { Name => 'ProjectionBoundsBottom',ValueConv => '$val / 4294967296' },
1017    3 => { Name => 'ProjectionBoundsLeft',  ValueConv => '$val / 4294967296' },
1018    4 => { Name => 'ProjectionBoundsRight', ValueConv => '$val / 4294967296' },
1019);
1020
1021# 'btrt' atom information (ref http://lists.freedesktop.org/archives/gstreamer-commits/2011-October/054459.html)
1022%Image::ExifTool::QuickTime::Bitrate = (
1023    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1024    GROUPS => { 2 => 'Video' },
1025    FORMAT => 'int32u',
1026    PRIORITY => 0, # often filled with zeros
1027    0 => 'BufferSize',
1028    1 => 'MaxBitrate',
1029    2 => 'AverageBitrate',
1030);
1031
1032# 'clap' atom information (ref https://developer.apple.com/library/mac/technotes/tn2162/_index.html#//apple_ref/doc/uid/DTS40013070-CH1-TNTAG9)
1033%Image::ExifTool::QuickTime::CleanAperture = (
1034    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1035    GROUPS => { 2 => 'Video' },
1036    FORMAT => 'rational64s',
1037    0 => 'CleanApertureWidth',
1038    1 => 'CleanApertureHeight',
1039    2 => 'CleanApertureOffsetX',
1040    3 => 'CleanApertureOffsetY',
1041);
1042
1043# preview data block
1044%Image::ExifTool::QuickTime::Preview = (
1045    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1046    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
1047    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
1048    GROUPS => { 2 => 'Image' },
1049    FORMAT => 'int16u',
1050    0 => {
1051        Name => 'PreviewDate',
1052        Format => 'int32u',
1053        Groups => { 2 => 'Time' },
1054        %timeInfo,
1055    },
1056    2 => 'PreviewVersion',
1057    3 => {
1058        Name => 'PreviewAtomType',
1059        Format => 'string[4]',
1060    },
1061    5 => 'PreviewAtomIndex',
1062);
1063
1064# movie atoms
1065%Image::ExifTool::QuickTime::Movie = (
1066    PROCESS_PROC => \&ProcessMOV,
1067    WRITE_PROC => \&WriteQuickTime,
1068    GROUPS => { 2 => 'Video' },
1069    mvhd => {
1070        Name => 'MovieHeader',
1071        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MovieHeader' },
1072    },
1073    trak => {
1074        Name => 'Track',
1075        CanCreate => 0, # don't create this atom
1076        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Track' },
1077    },
1078    udta => {
1079        Name => 'UserData',
1080        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::UserData' },
1081    },
1082    meta => { # 'meta' is found here in my EX-F1 MOV sample - PH
1083        Name => 'Meta',
1084        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1085    },
1086    iods => {
1087        Name => 'InitialObjectDescriptor',
1088        Flags => ['Binary','Unknown'],
1089    },
1090    uuid => [
1091        { #11 (MP4 files) (also found in QuickTime::Track)
1092            Name => 'UUID-USMT',
1093            Condition => '$$valPt=~/^USMT!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
1094            SubDirectory => {
1095                TagTable => 'Image::ExifTool::QuickTime::UserMedia',
1096                Start => 16,
1097            },
1098        },
1099        { #PH (Canon SX280)
1100            Name => 'UUID-Canon',
1101            Condition => '$$valPt=~/^\x85\xc0\xb6\x87\x82\x0f\x11\xe0\x81\x11\xf4\xce\x46\x2b\x6a\x48/',
1102            SubDirectory => {
1103                TagTable => 'Image::ExifTool::Canon::uuid',
1104                Start => 16,
1105            },
1106        },
1107        {
1108            Name => 'UUID-Unknown',
1109            %unknownInfo,
1110        },
1111    ],
1112    cmov => {
1113        Name => 'CompressedMovie',
1114        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::CMovie' },
1115    },
1116    htka => { # (written by HTC One M8 in slow-motion 1280x720 video - PH)
1117        Name => 'HTCTrack',
1118        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Track' },
1119    },
1120   'gps ' => {  # GPS data written by Novatek cameras (parsed in QuickTimeStream.pl)
1121        Name => 'GPSDataList',
1122        Unknown => 1,
1123        Binary => 1,
1124    },
1125    meco => { #ISO14496-12:2015
1126        Name => 'OtherMeta',
1127        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::OtherMeta' },
1128    },
1129    # prfl - Profile (ref 12)
1130    # clip - clipping --> contains crgn (clip region) (ref 12)
1131    # mvex - movie extends --> contains mehd (movie extends header), trex (track extends) (ref 14)
1132    # ICAT - 4 bytes: "6350" (Nikon CoolPix S6900), "6500" (Panasonic FT7)
1133);
1134
1135# (ref CFFMediaFormat-2_1.pdf)
1136%Image::ExifTool::QuickTime::MovieFragment = (
1137    PROCESS_PROC => \&ProcessMOV,
1138    WRITE_PROC => \&WriteQuickTime,
1139    GROUPS => { 2 => 'Video' },
1140    mfhd => {
1141        Name => 'MovieFragmentHeader',
1142        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MovieFragHdr' },
1143    },
1144    traf => {
1145        Name => 'TrackFragment',
1146        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackFragment' },
1147    },
1148    meta => { #ISO14496-12:2015
1149        Name => 'Meta',
1150        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1151    },
1152);
1153
1154# (ref CFFMediaFormat-2_1.pdf)
1155%Image::ExifTool::QuickTime::MovieFragHdr = (
1156    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1157    GROUPS => { 2 => 'Video' },
1158    FORMAT => 'int32u',
1159    1 => 'MovieFragmentSequence',
1160);
1161
1162# (ref CFFMediaFormat-2_1.pdf)
1163%Image::ExifTool::QuickTime::TrackFragment = (
1164    PROCESS_PROC => \&ProcessMOV,
1165    WRITE_PROC => \&WriteQuickTime,
1166    GROUPS => { 2 => 'Video' },
1167    meta => { #ISO14496-12:2015
1168        Name => 'Meta',
1169        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1170    },
1171    # tfhd - track fragment header
1172    # edts - edits --> contains elst (edit list) (ref PH)
1173    # tfdt - track fragment base media decode time
1174    # trik - trick play box
1175    # trun - track fragment run box
1176    # avcn - AVC NAL unit storage box
1177    # secn - sample encryption box
1178    # saio - sample auxiliary information offsets box
1179    # sbgp - sample to group box
1180    # sgpd - sample group description box
1181    # sdtp - independent and disposable samples (ref 5)
1182    # subs - sub-sample information (ref 5)
1183);
1184
1185# movie header data block
1186%Image::ExifTool::QuickTime::MovieHeader = (
1187    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1188    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
1189    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
1190    GROUPS => { 2 => 'Video' },
1191    FORMAT => 'int32u',
1192    DATAMEMBER => [ 0, 1, 2, 3, 4 ],
1193    0 => {
1194        Name => 'MovieHeaderVersion',
1195        Format => 'int8u',
1196        RawConv => '$$self{MovieHeaderVersion} = $val',
1197    },
1198    1 => {
1199        Name => 'CreateDate',
1200        Groups => { 2 => 'Time' },
1201        %timeInfo,
1202        RawConv => q{
1203            my $offset = (66 * 365 + 17) * 24 * 3600;
1204            if ($val >= $offset or $$self{OPTIONS}{QuickTimeUTC}) {
1205                $val -= $offset;
1206            } elsif ($val and not $$self{IsWriting}) {
1207                $self->WarnOnce('Patched incorrect time zero for QuickTime date/time tag',1);
1208            }
1209            return $$self{CreateDate} = $val;
1210        },
1211        # this is int64u if MovieHeaderVersion == 1 (ref 13)
1212        Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
1213    },
1214    2 => {
1215        Name => 'ModifyDate',
1216        Groups => { 2 => 'Time' },
1217        %timeInfo,
1218        # this is int64u if MovieHeaderVersion == 1 (ref 13)
1219        Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
1220    },
1221    3 => {
1222        Name => 'TimeScale',
1223        RawConv => '$$self{TimeScale} = $val',
1224    },
1225    4 => {
1226        Name => 'Duration',
1227        %durationInfo,
1228        # this is int64u if MovieHeaderVersion == 1 (ref 13)
1229        Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
1230    },
1231    5 => {
1232        Name => 'PreferredRate',
1233        ValueConv => '$val / 0x10000',
1234    },
1235    6 => {
1236        Name => 'PreferredVolume',
1237        Format => 'int16u',
1238        ValueConv => '$val / 256',
1239        PrintConv => 'sprintf("%.2f%%", $val * 100)',
1240    },
1241    9 => {
1242        Name => 'MatrixStructure',
1243        Format => 'fixed32s[9]',
1244        # (the right column is fixed 2.30 instead of 16.16)
1245        ValueConv => q{
1246            my @a = split ' ',$val;
1247            $_ /= 0x4000 foreach @a[2,5,8];
1248            return "@a";
1249        },
1250    },
1251    18 => { Name => 'PreviewTime',      %durationInfo },
1252    19 => { Name => 'PreviewDuration',  %durationInfo },
1253    20 => { Name => 'PosterTime',       %durationInfo },
1254    21 => { Name => 'SelectionTime',    %durationInfo },
1255    22 => { Name => 'SelectionDuration',%durationInfo },
1256    23 => { Name => 'CurrentTime',      %durationInfo },
1257    24 => 'NextTrackID',
1258);
1259
1260# track atoms
1261%Image::ExifTool::QuickTime::Track = (
1262    PROCESS_PROC => \&ProcessMOV,
1263    WRITE_PROC => \&WriteQuickTime,
1264    GROUPS => { 1 => 'Track#', 2 => 'Video' },
1265    tkhd => {
1266        Name => 'TrackHeader',
1267        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackHeader' },
1268    },
1269    udta => {
1270        Name => 'UserData',
1271        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::UserData' },
1272    },
1273    mdia => { #MP4
1274        Name => 'Media',
1275        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Media' },
1276    },
1277    meta => { #PH (MOV)
1278        Name => 'Meta',
1279        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1280    },
1281    tref => {
1282        Name => 'TrackRef',
1283        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackRef' },
1284    },
1285    tapt => {
1286        Name => 'TrackAperture',
1287        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackAperture' },
1288    },
1289    uuid => [
1290        { #11 (MP4 files) (also found in QuickTime::Movie)
1291            Name => 'UUID-USMT',
1292            Condition => '$$valPt=~/^USMT!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
1293            SubDirectory => {
1294                TagTable => 'Image::ExifTool::QuickTime::UserMedia',
1295                Start => 16,
1296            },
1297        },
1298        { #https://github.com/google/spatial-media/blob/master/docs/spherical-video-rfc.md
1299            Name => 'SphericalVideoXML',
1300            Condition => '$$valPt=~/^\xff\xcc\x82\x63\xf8\x55\x4a\x93\x88\x14\x58\x7a\x02\x52\x1f\xdd/',
1301            WriteGroup => 'GSpherical', # write only GSpherical XMP tags here
1302            HandlerType => 'vide',      # only write in video tracks
1303            SubDirectory => {
1304                TagTable => 'Image::ExifTool::XMP::Main',
1305                Start => 16,
1306                WriteProc => 'Image::ExifTool::XMP::WriteGSpherical',
1307            },
1308        },
1309        {
1310            Name => 'UUID-Unknown',
1311            %unknownInfo,
1312        },
1313    ],
1314    meco => { #ISO14492-12:2015 pg 83
1315        Name => 'OtherMeta',
1316        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::OtherMeta' },
1317    },
1318    # edts - edits --> contains elst (edit list)
1319    # clip - clipping --> contains crgn (clip region)
1320    # matt - track matt --> contains kmat (compressed matt)
1321    # load - track loading settings
1322    # imap - track input map --> contains '  in' --> contains '  ty', obid
1323    # prfl - Profile (ref 12)
1324);
1325
1326# track header data block
1327%Image::ExifTool::QuickTime::TrackHeader = (
1328    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1329    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
1330    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
1331    GROUPS => { 1 => 'Track#', 2 => 'Video' },
1332    FORMAT => 'int32u',
1333    DATAMEMBER => [ 0, 1, 2, 5, 7 ],
1334    0 => {
1335        Name => 'TrackHeaderVersion',
1336        Format => 'int8u',
1337        Priority => 0,
1338        RawConv => '$$self{TrackHeaderVersion} = $val',
1339    },
1340    1 => {
1341        Name => 'TrackCreateDate',
1342        Priority => 0,
1343        Groups => { 2 => 'Time' },
1344        %timeInfo,
1345        # this is int64u if TrackHeaderVersion == 1 (ref 13)
1346        Hook => '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
1347    },
1348    2 => {
1349        Name => 'TrackModifyDate',
1350        Priority => 0,
1351        Groups => { 2 => 'Time' },
1352        %timeInfo,
1353        # this is int64u if TrackHeaderVersion == 1 (ref 13)
1354        Hook => '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
1355    },
1356    3 => {
1357        Name => 'TrackID',
1358        Priority => 0,
1359    },
1360    5 => {
1361        Name => 'TrackDuration',
1362        Priority => 0,
1363        %durationInfo,
1364        # this is int64u if TrackHeaderVersion == 1 (ref 13)
1365        Hook => '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
1366    },
1367    7 => { # (used only for writing MatrixStructure)
1368        Name => 'ImageSizeLookahead',
1369        Hidden => 1,
1370        Format => 'int32u[14]',
1371        RawConv => '$$self{ImageSizeLookahead} = $val; undef',
1372    },
1373    8 => {
1374        Name => 'TrackLayer',
1375        Format => 'int16u',
1376        Priority => 0,
1377    },
1378    9 => {
1379        Name => 'TrackVolume',
1380        Format => 'int16u',
1381        Priority => 0,
1382        ValueConv => '$val / 256',
1383        PrintConv => 'sprintf("%.2f%%", $val * 100)',
1384    },
1385    10 => {
1386        Name => 'MatrixStructure',
1387        Format => 'fixed32s[9]',
1388        Notes => 'writable for the video track via the Composite Rotation tag',
1389        Writable => 1,
1390        Protected => 1,
1391        Permanent => 1,
1392        # only set rotation if image size is non-zero
1393        RawConvInv => \&GetMatrixStructure,
1394        # (the right column is fixed 2.30 instead of 16.16)
1395        ValueConv => q{
1396            my @a = split ' ',$val;
1397            $_ /= 0x4000 foreach @a[2,5,8];
1398            return "@a";
1399        },
1400        ValueConvInv => q{
1401            my @a = split ' ',$val;
1402            $_ *= 0x4000 foreach @a[2,5,8];
1403            return "@a";
1404        },
1405    },
1406    19 => {
1407        Name => 'ImageWidth',
1408        Priority => 0,
1409        RawConv => \&FixWrongFormat,
1410    },
1411    20 => {
1412        Name => 'ImageHeight',
1413        Priority => 0,
1414        RawConv => \&FixWrongFormat,
1415    },
1416);
1417
1418# user data atoms
1419%Image::ExifTool::QuickTime::UserData = (
1420    PROCESS_PROC => \&ProcessMOV,
1421    WRITE_PROC => \&WriteQuickTime,
1422    CHECK_PROC => \&CheckQTValue,
1423    GROUPS => { 1 => 'UserData', 2 => 'Video' },
1424    WRITABLE => 1,
1425    PREFERRED => 1, # (preferred over Keys tags when writing)
1426    FORMAT => 'string',
1427    WRITE_GROUP => 'UserData',
1428    LANG_INFO => \&GetLangInfo,
1429    NOTES => q{
1430        Tag ID's beginning with the copyright symbol (hex 0xa9) are multi-language
1431        text.  Alternate language tags are accessed by adding a dash followed by a
1432        3-character ISO 639-2 language code to the tag name.  ExifTool will extract
1433        any multi-language user data tags found, even if they aren't in this table.
1434        Note when creating new tags,
1435        L<ItemList|Image::ExifTool::TagNames/QuickTime ItemList Tags> tags are
1436        preferred over these, so to create the tag when a same-named ItemList tag
1437        exists, either "UserData" must be specified (eg. C<-UserData:Artist=Monet>
1438        on the command line), or the PREFERRED level must be changed via
1439        L<the config file|../config.html#PREF>.
1440    },
1441    "\xa9cpy" => { Name => 'Copyright',  Groups => { 2 => 'Author' } },
1442    "\xa9day" => {
1443        Name => 'ContentCreateDate',
1444        Groups => { 2 => 'Time' },
1445        Shift => 'Time',
1446        # handle values in the form "2010-02-12T13:27:14-0800" (written by Apple iPhone)
1447        ValueConv => q{
1448            require Image::ExifTool::XMP;
1449            $val =  Image::ExifTool::XMP::ConvertXMPDate($val);
1450            $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
1451            return $val;
1452        },
1453        ValueConvInv => q{
1454            require Image::ExifTool::XMP;
1455            my $tmp = Image::ExifTool::XMP::FormatXMPDate($val);
1456            ($val = $tmp) =~ s/([-+]\d{2}):(\d{2})$/$1$2/ if defined $tmp; # remove time zone colon
1457            return $val;
1458        },
1459        PrintConv => '$self->ConvertDateTime($val)',
1460        PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
1461    },
1462    "\xa9ART" => 'Artist', #PH (iTunes 8.0.2)
1463    "\xa9alb" => 'Album', #PH (iTunes 8.0.2)
1464    "\xa9arg" => 'Arranger', #12
1465    "\xa9ark" => 'ArrangerKeywords', #12
1466    "\xa9cmt" => 'Comment', #PH (iTunes 8.0.2)
1467    "\xa9cok" => 'ComposerKeywords', #12
1468    "\xa9com" => 'Composer', #12
1469    "\xa9dir" => 'Director', #12
1470    "\xa9ed1" => 'Edit1',
1471    "\xa9ed2" => 'Edit2',
1472    "\xa9ed3" => 'Edit3',
1473    "\xa9ed4" => 'Edit4',
1474    "\xa9ed5" => 'Edit5',
1475    "\xa9ed6" => 'Edit6',
1476    "\xa9ed7" => 'Edit7',
1477    "\xa9ed8" => 'Edit8',
1478    "\xa9ed9" => 'Edit9',
1479    "\xa9fmt" => 'Format',
1480    "\xa9gen" => 'Genre', #PH (iTunes 8.0.2)
1481    "\xa9grp" => 'Grouping', #PH (NC)
1482    "\xa9inf" => 'Information',
1483    "\xa9isr" => 'ISRCCode', #12
1484    "\xa9lab" => 'RecordLabelName', #12
1485    "\xa9lal" => 'RecordLabelURL', #12
1486    "\xa9lyr" => 'Lyrics', #PH (NC)
1487    "\xa9mak" => 'Make', #12
1488    "\xa9mal" => 'MakerURL', #12
1489    "\xa9mod" => 'Model', #PH
1490    "\xa9nam" => 'Title', #12
1491    "\xa9pdk" => 'ProducerKeywords', #12
1492    "\xa9phg" => 'RecordingCopyright', #12
1493    "\xa9prd" => 'Producer',
1494    "\xa9prf" => 'Performers',
1495    "\xa9prk" => 'PerformerKeywords', #12
1496    "\xa9prl" => 'PerformerURL',
1497    "\xa9req" => 'Requirements',
1498    "\xa9snk" => 'SubtitleKeywords', #12
1499    "\xa9snm" => 'Subtitle', #12
1500    "\xa9src" => 'SourceCredits', #12
1501    "\xa9swf" => 'SongWriter', #12
1502    "\xa9swk" => 'SongWriterKeywords', #12
1503    "\xa9swr" => 'SoftwareVersion', #12
1504    "\xa9too" => 'Encoder', #PH (NC)
1505    "\xa9trk" => 'Track', #PH (NC)
1506    "\xa9wrt" => { Name => 'Composer', Avoid => 1 }, # ("\xa9com" is preferred in UserData)
1507    "\xa9xyz" => { #PH (iPhone 3GS)
1508        Name => 'GPSCoordinates',
1509        Groups => { 2 => 'Location' },
1510        ValueConv => \&ConvertISO6709,
1511        ValueConvInv => \&ConvInvISO6709,
1512        PrintConv => \&PrintGPSCoordinates,
1513        PrintConvInv => \&PrintInvGPSCoordinates,
1514    },
1515    # \xa9 tags written by DJI Phantom 3: (ref PH)
1516    "\xa9xsp" => 'SpeedX', #PH (guess)
1517    "\xa9ysp" => 'SpeedY', #PH (guess)
1518    "\xa9zsp" => 'SpeedZ', #PH (guess)
1519    "\xa9fpt" => 'Pitch', #PH
1520    "\xa9fyw" => 'Yaw', #PH
1521    "\xa9frl" => 'Roll', #PH
1522    "\xa9gpt" => 'CameraPitch', #PH
1523    "\xa9gyw" => 'CameraYaw', #PH
1524    "\xa9grl" => 'CameraRoll', #PH
1525    "\xa9enc" => 'EncoderID', #PH (forum9271)
1526    # and the following entries don't have the proper 4-byte header for \xa9 tags:
1527    "\xa9dji" => { Name => 'UserData_dji', Format => 'undef', Binary => 1, Unknown => 1, Hidden => 1 },
1528    "\xa9res" => { Name => 'UserData_res', Format => 'undef', Binary => 1, Unknown => 1, Hidden => 1 },
1529    "\xa9uid" => { Name => 'UserData_uid', Format => 'undef', Binary => 1, Unknown => 1, Hidden => 1 },
1530    "\xa9mdl" => {
1531        Name => 'Model',
1532        Notes => 'non-standard-format DJI tag',
1533        Format => 'string',
1534        Avoid => 1,
1535    },
1536    # end DJI tags
1537    name => 'Name',
1538    WLOC => {
1539        Name => 'WindowLocation',
1540        Format => 'int16u',
1541    },
1542    LOOP => {
1543        Name => 'LoopStyle',
1544        Format => 'int32u',
1545        PrintConv => {
1546            1 => 'Normal',
1547            2 => 'Palindromic',
1548        },
1549    },
1550    SelO => {
1551        Name => 'PlaySelection',
1552        Format => 'int8u',
1553    },
1554    AllF => {
1555        Name => 'PlayAllFrames',
1556        Format => 'int8u',
1557    },
1558    meta => {
1559        Name => 'Meta',
1560        SubDirectory => {
1561            TagTable => 'Image::ExifTool::QuickTime::Meta',
1562            Start => 4, # must skip 4-byte version number header
1563        },
1564    },
1565   'ptv '=> {
1566        Name => 'PrintToVideo',
1567        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Video' },
1568    },
1569    hnti => {
1570        Name => 'HintInfo',
1571        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HintInfo' },
1572    },
1573    hinf => {
1574        Name => 'HintTrackInfo',
1575        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HintTrackInfo' },
1576    },
1577    hinv => 'HintVersion', #PH (guess)
1578    XMP_ => { #PH (Adobe CS3 Bridge)
1579        Name => 'XMP',
1580        WriteGroup => 'XMP',    # (write main tags here)
1581        # *** this is where ExifTool writes XMP in MOV videos (as per XMP spec) ***
1582        SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
1583    },
1584    # the following are 3gp tags, references:
1585    # http://atomicparsley.sourceforge.net
1586    # http://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_25/Docs/
1587    # (note that all %langText3gp tags are Avoid => 1)
1588    cprt => { Name => 'Copyright',  %langText3gp, Groups => { 2 => 'Author' } },
1589    auth => { Name => 'Author',     %langText3gp, Groups => { 2 => 'Author' } },
1590    titl => { Name => 'Title',      %langText3gp },
1591    dscp => { Name => 'Description',%langText3gp },
1592    perf => { Name => 'Performer',  %langText3gp },
1593    gnre => { Name => 'Genre',      %langText3gp },
1594    albm => { Name => 'Album',      %langText3gp },
1595    coll => { Name => 'CollectionName', %langText3gp }, #17
1596    rtng => {
1597        Name => 'Rating',
1598        Writable => 'undef',
1599        Avoid => 1,
1600        # (4-byte flags, 4-char entity, 4-char criteria, 2-byte lang, string)
1601        IText => 14, # (14 bytes before string)
1602        Notes => 'string in the form "Entity=XXXX Criteria=XXXX XXXXX", used in 3gp videos',
1603        ValueConv => '$val=~s/^(.{4})(.{4})/Entity=$1 Criteria=$2 /i; $val',
1604        ValueConvInv => '$val=~s/Entity=(.{4}) Criteria=(.{4}) ?/$1$2/i; $val',
1605    },
1606    clsf => {
1607        Name => 'Classification',
1608        Writable => 'undef',
1609        Avoid => 1,
1610        # (4-byte flags, 4-char entity, 2-byte index, 2-byte lang, string)
1611        IText => 12,
1612        Notes => 'string in the form "Entity=XXXX Index=### XXXXX", used in 3gp videos',
1613        ValueConv => '$val=~s/^(.{4})(.{2})/"Entity=$1 Index=".unpack("n",$2)." "/ie; $val',
1614        ValueConvInv => '$val=~s/Entity=(.{4}) Index=(\d+) ?/$1.pack("n",$2)/ie; $val',
1615    },
1616    kywd => {
1617        Name => 'Keywords',
1618        # (4 byte flags, 2-byte lang, 1-byte count, count x pascal strings, ref 17)
1619        # (but I have also seen a simple string written by iPhone, so don't make writable yet)
1620        Notes => "not writable because Apple doesn't follow the 3gp specification",
1621        RawConv => q{
1622            my $sep = $self->Options('ListSep');
1623            return join($sep, split /\0+/, $val) unless $val =~ /^\0/; # (iPhone)
1624            return '<err>' unless length $val >= 7;
1625            my $lang = Image::ExifTool::QuickTime::UnpackLang(Get16u(\$val, 4));
1626            $lang = $lang ? "($lang) " : '';
1627            my $num = Get8u(\$val, 6);
1628            my ($i, @vals);
1629            my $pos = 7;
1630            for ($i=0; $i<$num; ++$i) {
1631                last if $pos >= length $val;
1632                my $len = Get8u(\$val, $pos++);
1633                last if $pos + $len > length $val;
1634                my $v = substr($val, $pos, $len);
1635                $v = $self->Decode($v, 'UCS2') if $v =~ /^\xfe\xff/;
1636                push @vals, $v;
1637                $pos += $len;
1638            }
1639            return $lang . join($sep, @vals);
1640        },
1641    },
1642    loci => {
1643        Name => 'LocationInformation',
1644        Groups => { 2 => 'Location' },
1645        Writable => 'undef',
1646        IText => 6,
1647        Avoid => 1,
1648        NoDecode => 1, # (we'll decode the data ourself)
1649        Notes => q{
1650            string in the form "XXXXX Role=XXX Lat=XXX Lon=XXX Alt=XXX Body=XXX
1651            Notes=XXX", used in 3gp videos
1652        },
1653        # (4-byte flags, 2-byte lang, location string, 1-byte role, 4-byte fixed longitude,
1654        #  4-byte fixed latitude, 4-byte fixed altitude, body string, notes string)
1655        RawConv => q{
1656            my $str;
1657            if ($val =~ /^\xfe\xff/) {
1658                $val =~ s/^(\xfe\xff(.{2})*?)\0\0//s or return '<err>';
1659                $str = $self->Decode($1, 'UCS2');
1660            } else {
1661                $val =~ s/^(.*?)\0//s or return '<err>';
1662                $str = $self->Decode($1, 'UTF8');
1663            }
1664            $str = '(none)' unless length $str;
1665            return '<err>' if length $val < 13;
1666            my $role = Get8u(\$val, 0);
1667            my $lon = GetFixed32s(\$val, 1);
1668            my $lat = GetFixed32s(\$val, 5);
1669            my $alt = GetFixed32s(\$val, 9);
1670            my $roleStr = {0=>'shooting',1=>'real',2=>'fictional',3=>'reserved'}->{$role};
1671            $str .= ' Role=' . ($roleStr || "unknown($role)");
1672            $str .= sprintf(' Lat=%.5f Lon=%.5f Alt=%.2f', $lat, $lon, $alt);
1673            $val = substr($val, 13);
1674            if ($val =~ s/^(\xfe\xff(.{2})*?)\0\0//s) {
1675                $str .= ' Body=' . $self->Decode($1, 'UCS2');
1676            } elsif ($val =~ s/^(.*?)\0//s) {
1677                $str .= ' Body=' . $self->Decode($1, 'UTF8');
1678            }
1679            if ($val =~ s/^(\xfe\xff(.{2})*?)\0\0//s) {
1680                $str .= ' Notes=' . $self->Decode($1, 'UCS2');
1681            } elsif ($val =~ s/^(.*?)\0//s) {
1682                $str .= ' Notes=' . $self->Decode($1, 'UTF8');
1683            }
1684            return $str;
1685        },
1686        RawConvInv => q{
1687            my ($role, $lat, $lon, $alt, $body, $note);
1688            $lat = $1 if $val =~ s/ Lat=([-+]?[.\d]+)//i;
1689            $lon = $1 if $val =~ s/ Lon=([-+]?[.\d]+)//i;
1690            $alt = $1 if $val =~ s/ Alt=([-+]?[.\d]+)//i;
1691            $note = $val =~ s/ Notes=(.*)//i ? $1 : '';
1692            $body = $val =~ s/ Body=(.*)//i ? $1 : '';
1693            $role = $val =~ s/ Role=(.*)//i ? $1 : '';
1694            $val = '' if $val eq '(none)';
1695            $role = {shooting=>0,real=>1,fictional=>2}->{lc $role} || 0;
1696            return $self->Encode($val, 'UTF8') . "\0" . Set8u($role) .
1697                   SetFixed32s(defined $lon ? $lon : 999) .
1698                   SetFixed32s(defined $lat ? $lat : 999) .
1699                   SetFixed32s(defined $alt ? $alt : 0) .
1700                   $self->Encode($body) . "\0" .
1701                   $self->Encode($note) . "\0";
1702        },
1703    },
1704    yrrc => {
1705        Name => 'Year',
1706        Writable => 'undef',
1707        Groups => { 2 => 'Time' },
1708        Avoid => 1,
1709        Notes => 'used in 3gp videos',
1710        ValueConv => 'length($val) >= 6 ? unpack("x4n",$val) : "<err>"',
1711        ValueConvInv => 'pack("Nn",0,$val)',
1712    },
1713    urat => { #17
1714        Name => 'UserRating',
1715        Writable => 'undef',
1716        Notes => 'used in 3gp videos',
1717        Avoid => 1,
1718        ValueConv => q{
1719            return '<err>' unless length $val >= 8;
1720            unpack('x7C', $val);
1721        },
1722        ValueConvInv => 'pack("N2",0,$val)',
1723    },
1724    # tsel - TrackSelection (ref 17)
1725    # Apple tags (ref 16[dead] -- see ref 25 instead)
1726    angl => { Name => 'CameraAngle',  Format => 'string' }, # (NC)
1727    clfn => { Name => 'ClipFileName', Format => 'string' }, # (NC)
1728    clid => { Name => 'ClipID',       Format => 'string' }, # (NC)
1729    cmid => { Name => 'CameraID',     Format => 'string' }, # (NC)
1730    cmnm => { # (NC)
1731        Name => 'Model',
1732        Description => 'Camera Model Name',
1733        Avoid => 1,
1734        Format => 'string', # (necessary to remove the trailing NULL)
1735    },
1736    date => {
1737        Name => 'DateTimeOriginal',
1738        Description => 'Date/Time Original',
1739        Groups => { 2 => 'Time' },
1740        Notes => q{
1741            Apple Photos has been reported to show a crazy date/time for some MP4 files
1742            containing this tag, but perhaps only if it is missing a time zone
1743        }, #forum10690/11125
1744        Shift => 'Time',
1745        ValueConv => q{
1746            require Image::ExifTool::XMP;
1747            $val =  Image::ExifTool::XMP::ConvertXMPDate($val);
1748            $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
1749            return $val;
1750        },
1751        ValueConvInv => q{
1752            require Image::ExifTool::XMP;
1753            $val =  Image::ExifTool::XMP::FormatXMPDate($val);
1754            $val =~ s/([-+]\d{2}):(\d{2})$/$1$2/; # remove time zone colon
1755            return $val;
1756        },
1757        PrintConv => '$self->ConvertDateTime($val)',
1758        PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
1759    },
1760    manu => { # (SX280)
1761        Name => 'Make',
1762        Avoid => 1,
1763        # (with Canon there are 6 unknown bytes before the model: "\0\0\0\0\x15\xc7")
1764        RawConv => '$val=~s/^\0{4}..//s; $val=~s/\0.*//; $val',
1765    },
1766    modl => { # (Samsung GT-S8530, Canon SX280)
1767        Name => 'Model',
1768        Description => 'Camera Model Name',
1769        Avoid => 1,
1770        # (with Canon there are 6 unknown bytes before the model: "\0\0\0\0\x15\xc7")
1771        RawConv => '$val=~s/^\0{4}..//s; $val=~s/\0.*//; $val',
1772    },
1773    reel => { Name => 'ReelName',     Format => 'string' }, # (NC)
1774    scen => { Name => 'Scene',        Format => 'string' }, # (NC)
1775    shot => { Name => 'ShotName',     Format => 'string' }, # (NC)
1776    slno => { Name => 'SerialNumber', Format => 'string' }, # (NC)
1777    apmd => { Name => 'ApertureMode', Format => 'undef' }, #20
1778    kgtt => { #http://lists.ffmpeg.org/pipermail/ffmpeg-devel-irc/2012-June/000707.html
1779        # 'TrackType' will expand to 'Track#Type' when found inside a track
1780        Name => 'TrackType',
1781        # set flag to process this as international text
1782        # even though the tag ID doesn't start with 0xa9
1783        IText => 4, # IText with 4-byte header
1784    },
1785    chpl => { # (Nero chapter list)
1786        Name => 'ChapterList',
1787        ValueConv => \&ConvertChapterList,
1788        PrintConv => \&PrintChapter,
1789    },
1790    # ndrm - 7 bytes (0 0 0 1 0 0 0) Nero Digital Rights Management? (PH)
1791    # other non-Apple tags (ref 16)
1792    # hpix - HipixRichPicture (ref 16, HIPIX)
1793    # strk - sub-track information (ref 16, ISO)
1794#
1795# Manufacturer-specific metadata
1796#
1797    TAGS => [ #PH
1798        # these tags were initially discovered in a Pentax movie,
1799        # but similar information is found in videos from other manufacturers
1800        {
1801            Name => 'FujiFilmTags',
1802            Condition => '$$valPt =~ /^FUJIFILM DIGITAL CAMERA\0/',
1803            SubDirectory => {
1804                TagTable => 'Image::ExifTool::FujiFilm::MOV',
1805                ByteOrder => 'LittleEndian',
1806            },
1807        },
1808        {
1809            Name => 'KodakTags',
1810            Condition => '$$valPt =~ /^EASTMAN KODAK COMPANY/',
1811            SubDirectory => {
1812                TagTable => 'Image::ExifTool::Kodak::MOV',
1813                ByteOrder => 'LittleEndian',
1814            },
1815        },
1816        {
1817            Name => 'KonicaMinoltaTags',
1818            Condition => '$$valPt =~ /^KONICA MINOLTA DIGITAL CAMERA/',
1819            SubDirectory => {
1820                TagTable => 'Image::ExifTool::Minolta::MOV1',
1821                ByteOrder => 'LittleEndian',
1822            },
1823        },
1824        {
1825            Name => 'MinoltaTags',
1826            Condition => '$$valPt =~ /^MINOLTA DIGITAL CAMERA/',
1827            SubDirectory => {
1828                TagTable => 'Image::ExifTool::Minolta::MOV2',
1829                ByteOrder => 'LittleEndian',
1830            },
1831        },
1832        {
1833            Name => 'NikonTags',
1834            Condition => '$$valPt =~ /^NIKON DIGITAL CAMERA\0/',
1835            SubDirectory => {
1836                TagTable => 'Image::ExifTool::Nikon::MOV',
1837                ByteOrder => 'LittleEndian',
1838            },
1839        },
1840        {
1841            Name => 'OlympusTags1',
1842            Condition => '$$valPt =~ /^OLYMPUS DIGITAL CAMERA\0.{9}\x01\0/s',
1843            SubDirectory => {
1844                TagTable => 'Image::ExifTool::Olympus::MOV1',
1845                ByteOrder => 'LittleEndian',
1846            },
1847        },
1848        {
1849            Name => 'OlympusTags2',
1850            Condition => '$$valPt =~ /^OLYMPUS DIGITAL CAMERA(?!\0.{21}\x0a\0{3})/s',
1851            SubDirectory => {
1852                TagTable => 'Image::ExifTool::Olympus::MOV2',
1853                ByteOrder => 'LittleEndian',
1854            },
1855        },
1856        {
1857            Name => 'OlympusTags3',
1858            Condition => '$$valPt =~ /^OLYMPUS DIGITAL CAMERA\0/',
1859            SubDirectory => {
1860                TagTable => 'Image::ExifTool::Olympus::MP4',
1861                ByteOrder => 'LittleEndian',
1862            },
1863        },
1864        {
1865            Name => 'OlympusTags4',
1866            Condition => '$$valPt =~ /^.{16}OLYM\0/s',
1867            SubDirectory => {
1868                TagTable => 'Image::ExifTool::Olympus::MOV3',
1869                Start => 12,
1870            },
1871        },
1872        {
1873            Name => 'PentaxTags',
1874            Condition => '$$valPt =~ /^PENTAX DIGITAL CAMERA\0/',
1875            SubDirectory => {
1876                TagTable => 'Image::ExifTool::Pentax::MOV',
1877                ByteOrder => 'LittleEndian',
1878            },
1879        },
1880        {
1881            Name => 'SamsungTags',
1882            Condition => '$$valPt =~ /^SAMSUNG DIGITAL CAMERA\0/',
1883            SubDirectory => {
1884                TagTable => 'Image::ExifTool::Samsung::MP4',
1885                ByteOrder => 'LittleEndian',
1886            },
1887        },
1888        {
1889            Name => 'SanyoMOV',
1890            Condition => q{
1891                $$valPt =~ /^SANYO DIGITAL CAMERA\0/ and
1892                $self->{VALUE}->{FileType} eq "MOV"
1893            },
1894            SubDirectory => {
1895                TagTable => 'Image::ExifTool::Sanyo::MOV',
1896                ByteOrder => 'LittleEndian',
1897            },
1898        },
1899        {
1900            Name => 'SanyoMP4',
1901            Condition => q{
1902                $$valPt =~ /^SANYO DIGITAL CAMERA\0/ and
1903                $self->{VALUE}->{FileType} eq "MP4"
1904            },
1905            SubDirectory => {
1906                TagTable => 'Image::ExifTool::Sanyo::MP4',
1907                ByteOrder => 'LittleEndian',
1908            },
1909        },
1910        {
1911            Name => 'UnknownTags',
1912            Unknown => 1,
1913            Binary => 1
1914        },
1915    ],
1916    # ---- Canon ----
1917    CNCV => { Name => 'CompressorVersion', Format => 'string' }, #PH (5D Mark II)
1918    CNMN => {
1919        Name => 'Model', #PH (EOS 550D)
1920        Description => 'Camera Model Name',
1921        Avoid => 1,
1922        Format => 'string', # (necessary to remove the trailing NULL)
1923    },
1924    CNFV => { Name => 'FirmwareVersion', Format => 'string' }, #PH (EOS 550D)
1925    CNTH => { #PH (PowerShot S95)
1926        Name => 'CanonCNTH',
1927        SubDirectory => { TagTable => 'Image::ExifTool::Canon::CNTH' },
1928    },
1929    CNOP => { #PH (7DmkII)
1930        Name => 'CanonCNOP',
1931        SubDirectory => { TagTable => 'Image::ExifTool::Canon::CNOP' },
1932    },
1933    # CNDB - 2112 bytes (550D)
1934    # CNDM - 4 bytes - 0xff,0xd8,0xff,0xd9 (S95)
1935    # CNDG - 10232 bytes, mostly zeros (N100)
1936    # ---- Casio ----
1937    QVMI => { #PH
1938        Name => 'CasioQVMI',
1939        # Casio stores standard EXIF-format information in MOV videos (eg. EX-S880)
1940        SubDirectory => {
1941            TagTable => 'Image::ExifTool::Exif::Main',
1942            ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
1943            DirName => 'IFD0',
1944            Multi => 0, # (no NextIFD pointer)
1945            Start => 10,
1946            ByteOrder => 'BigEndian',
1947        },
1948    },
1949    # ---- FujiFilm ----
1950    FFMV => { #PH (FinePix HS20EXR)
1951        Name => 'FujiFilmFFMV',
1952        SubDirectory => { TagTable => 'Image::ExifTool::FujiFilm::FFMV' },
1953    },
1954    MVTG => { #PH (FinePix HS20EXR)
1955        Name => 'FujiFilmMVTG',
1956        SubDirectory => {
1957            TagTable => 'Image::ExifTool::Exif::Main',
1958            ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
1959            DirName => 'IFD0',
1960            Start => 16,
1961            Base => '$start',
1962            ByteOrder => 'LittleEndian',
1963        },
1964    },
1965    # ---- Garmin ---- (ref PH)
1966    uuid => [{
1967        Name => 'GarminSoftware', # (NC)
1968        Condition => '$$valPt =~ /^VIRBactioncamera/',
1969        RawConv => 'substr($val, 16)',
1970        RawConvInv => '"VIRBactioncamera$val"',
1971    },{
1972        # 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)
1973        Name => 'UUID-Unknown',
1974        Writable => 0,
1975        %unknownInfo,
1976    }],
1977    pmcc => {
1978        Name => 'GarminSettings',
1979        ValueConv => 'substr($val, 4)',
1980        ValueConvInv => '"\0\0\0\x01$val"',
1981    },
1982    # hmtp - "\0\0\0\x01" followed by 408 bytes of zero
1983    # vrin - "\0\0\0\x01" followed by 8 bytes of zero
1984    # ---- GoPro ---- (ref PH)
1985    GoPr => 'GoProType', # (Hero3+)
1986    FIRM => { Name => 'FirmwareVersion', Avoid => 1 }, # (Hero4)
1987    LENS => 'LensSerialNumber', # (Hero4)
1988    CAME => { # (Hero4)
1989        Name => 'SerialNumberHash',
1990        Description => 'Camera Serial Number Hash',
1991        ValueConv => 'unpack("H*",$val)',
1992        ValueConvInv => 'pack("H*",$val)',
1993    },
1994    # SETT? 12 bytes (Hero4)
1995    # MUID? 32 bytes (Hero4, starts with serial number hash)
1996    # HMMT? 404 bytes (Hero4, all zero)
1997    # BCID? 26 bytes (Hero5, all zero), 36 bytes GoPro Max
1998    # GUMI? 16 bytes (Hero5)
1999   "FOV\0" => 'FieldOfView', #forum8938 (Hero2) seen: "Wide"
2000    GPMF => {
2001        Name => 'GoProGPMF',
2002        SubDirectory => { TagTable => 'Image::ExifTool::GoPro::GPMF' },
2003    },
2004    # free (all zero)
2005    "\xa9TSC" => 'StartTimeScale', # (Hero6)
2006    "\xa9TSZ" => 'StartTimeSampleSize', # (Hero6)
2007    "\xa9TIM" => 'StartTimecode', #PH (NC)
2008    # --- HTC ----
2009    htcb => {
2010        Name => 'HTCBinary',
2011        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HTCBinary' },
2012    },
2013    # ---- Kodak ----
2014    DcMD => {
2015        Name => 'KodakDcMD',
2016        SubDirectory => { TagTable => 'Image::ExifTool::Kodak::DcMD' },
2017    },
2018    SNum => { Name => 'SerialNumber', Avoid => 1, Groups => { 2 => 'Camera' } },
2019    ptch => { Name => 'Pitch', Format => 'rational64s', Avoid => 1 }, # Units??
2020    _yaw => { Name => 'Yaw',   Format => 'rational64s', Avoid => 1 }, # Units??
2021    roll => { Name => 'Roll',  Format => 'rational64s', Avoid => 1 }, # Units??
2022    _cx_ => { Name => 'CX',    Format => 'rational64s', Unknown => 1 },
2023    _cy_ => { Name => 'CY',    Format => 'rational64s', Unknown => 1 },
2024    rads => { Name => 'Rads',  Format => 'rational64s', Unknown => 1 },
2025    lvlm => { Name => 'LevelMeter', Format => 'rational64s', Unknown => 1 }, # (guess)
2026    Lvlm => { Name => 'LevelMeter', Format => 'rational64s', Unknown => 1 }, # (guess)
2027    pose => { Name => 'pose', SubDirectory => { TagTable => 'Image::ExifTool::Kodak::pose' } },
2028    # AMBA => Ambarella AVC atom (unknown data written by Kodak Playsport video cam)
2029    # tmlp - 1 byte: 0 (PixPro SP360/4KVR360)
2030    # pivi - 72 bytes (PixPro SP360)
2031    # pive - 12 bytes (PixPro SP360)
2032    # loop - 4 bytes: 0 0 0 0 (PixPro 4KVR360)
2033    # m cm - 2 bytes: 0 0 (PixPro 4KVR360)
2034    # m ev - 2 bytes: 0 0 (PixPro SP360/4KVR360) (exposure comp?)
2035    # m vr - 2 bytes: 0 1 (PixPro 4KVR360) (virtual reality?)
2036    # m wb - 4 bytes: 0 0 0 0 (PixPro SP360/4KVR360) (white balance?)
2037    # mclr - 4 bytes: 0 0 0 0 (PixPro SP360/4KVR360)
2038    # mmtr - 4 bytes: 0,6 0 0 0 (PixPro SP360/4KVR360)
2039    # mflr - 4 bytes: 0 0 0 0 (PixPro SP360)
2040    # lvlm - 24 bytes (PixPro SP360)
2041    # Lvlm - 24 bytes (PixPro 4KVR360)
2042    # ufdm - 4 bytes: 0 0 0 1 (PixPro SP360)
2043    # mtdt - 1 byte: 0 (PixPro SP360/4KVR360)
2044    # gdta - 75240 bytes (PixPro SP360)
2045    # EIS1 - 4 bytes: 03 07 00 00 (PixPro 4KVR360)
2046    # EIS2 - 4 bytes: 04 97 00 00 (PixPro 4KVR360)
2047    # ---- LG ----
2048    adzc => { Name => 'Unknown_adzc', Unknown => 1, Hidden => 1, %langText }, # "false\0/","true\0/"
2049    adze => { Name => 'Unknown_adze', Unknown => 1, Hidden => 1, %langText }, # "false\0/"
2050    adzm => { Name => 'Unknown_adzm', Unknown => 1, Hidden => 1, %langText }, # "\x0e\x04/","\x10\x06"
2051    # ---- Microsoft ----
2052    Xtra => { #PH (microsoft)
2053        Name => 'MicrosoftXtra',
2054        WriteGroup => 'Microsoft',
2055        SubDirectory => {
2056            DirName => 'Microsoft',
2057            TagTable => 'Image::ExifTool::Microsoft::Xtra',
2058        },
2059    },
2060    # ---- Minolta ----
2061    MMA0 => { #PH (DiMage 7Hi)
2062        Name => 'MinoltaMMA0',
2063        SubDirectory => { TagTable => 'Image::ExifTool::Minolta::MMA' },
2064    },
2065    MMA1 => { #PH (Dimage A2)
2066        Name => 'MinoltaMMA1',
2067        SubDirectory => { TagTable => 'Image::ExifTool::Minolta::MMA' },
2068    },
2069    # ---- Nikon ----
2070    NCDT => { #PH
2071        Name => 'NikonNCDT',
2072        SubDirectory => { TagTable => 'Image::ExifTool::Nikon::NCDT' },
2073    },
2074    # ---- Olympus ----
2075    scrn => { #PH (TG-810)
2076        Name => 'OlympusPreview',
2077        Condition => '$$valPt =~ /^.{4}\xff\xd8\xff\xdb/s',
2078        SubDirectory => { TagTable => 'Image::ExifTool::Olympus::scrn' },
2079    },
2080    # ---- Panasonic/Leica ----
2081    PANA => { #PH
2082        Name => 'PanasonicPANA',
2083        SubDirectory => { TagTable => 'Image::ExifTool::Panasonic::PANA' },
2084    },
2085    LEIC => { #PH
2086        Name => 'LeicaLEIC',
2087        SubDirectory => { TagTable => 'Image::ExifTool::Panasonic::PANA' },
2088    },
2089    # ---- Pentax ----
2090    thmb => [ # (apparently defined by 3gpp, ref 16)
2091        { #PH (Pentax Q)
2092            Name => 'MakerNotePentax5a',
2093            Condition => '$$valPt =~ /^PENTAX \0II/',
2094            SubDirectory => {
2095                TagTable => 'Image::ExifTool::Pentax::Main',
2096                ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2097                Start => 10,
2098                Base => '$start - 10',
2099                ByteOrder => 'LittleEndian',
2100            },
2101        },{ #PH (TG-810)
2102            Name => 'OlympusThumbnail',
2103            Condition => '$$valPt =~ /^.{4}\xff\xd8\xff\xdb/s',
2104            SubDirectory => { TagTable => 'Image::ExifTool::Olympus::thmb' },
2105        },{ #17 (format is in bytes 3-7)
2106            Name => 'ThumbnailImage',
2107            Condition => '$$valPt =~ /^.{8}\xff\xd8\xff[\xdb\xe0]/s',
2108            Groups => { 2 => 'Preview' },
2109            RawConv => 'substr($val, 8)',
2110            Binary => 1,
2111        },{ #17 (format is in bytes 3-7)
2112            Name => 'ThumbnailPNG',
2113            Condition => '$$valPt =~ /^.{8}\x89PNG\r\n\x1a\n/s',
2114            Groups => { 2 => 'Preview' },
2115            RawConv => 'substr($val, 8)',
2116            Binary => 1,
2117        },{
2118            Name => 'UnknownThumbnail',
2119            Groups => { 2 => 'Preview' },
2120            Binary => 1,
2121        },
2122    ],
2123    PENT => { #PH
2124        Name => 'PentaxPENT',
2125        SubDirectory => {
2126            TagTable => 'Image::ExifTool::Pentax::PENT',
2127            ByteOrder => 'LittleEndian',
2128        },
2129    },
2130    PXTH => { #PH (Pentax K-01)
2131        Name => 'PentaxPreview',
2132        SubDirectory => { TagTable => 'Image::ExifTool::Pentax::PXTH' },
2133    },
2134    PXMN => [{ #PH (Pentax K-01)
2135        Name => 'MakerNotePentax5b',
2136        Condition => '$$valPt =~ /^PENTAX \0MM/',
2137        SubDirectory => {
2138            TagTable => 'Image::ExifTool::Pentax::Main',
2139            ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2140            Start => 10,
2141            Base => '$start - 10',
2142            ByteOrder => 'BigEndian',
2143        },
2144    },{ #PH (Pentax 645Z)
2145        Name => 'MakerNotePentax5c',
2146        Condition => '$$valPt =~ /^PENTAX \0II/',
2147        SubDirectory => {
2148            TagTable => 'Image::ExifTool::Pentax::Main',
2149            ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2150            Start => 10,
2151            Base => '$start - 10',
2152            ByteOrder => 'LittleEndian',
2153        },
2154    },{
2155        Name => 'MakerNotePentaxUnknown',
2156        Binary => 1,
2157    }],
2158    # ---- Ricoh ----
2159    RTHU => { #PH (GR)
2160        Name => 'PreviewImage',
2161        Groups => { 2 => 'Preview' },
2162        RawConv => '$self->ValidateImage(\$val, $tag)',
2163    },
2164    RMKN => { #PH (GR)
2165        Name => 'RicohRMKN',
2166        SubDirectory => {
2167            TagTable => 'Image::ExifTool::Exif::Main',
2168            ProcessProc => \&Image::ExifTool::ProcessTIFF, # (because ProcessMOV is default)
2169        },
2170    },
2171    '@mak' => { Name => 'Make',     Avoid => 1 },
2172    '@mod' => { Name => 'Model',    Avoid => 1 },
2173    '@swr' => { Name => 'SoftwareVersion', Avoid => 1 },
2174    '@day' => {
2175        Name => 'ContentCreateDate',
2176        Notes => q{
2177            some stupid Ricoh programmer used the '@' symbol instead of the copyright
2178            symbol in these tag ID's for the Ricoh Theta Z1 and maybe other models
2179        },
2180        Groups => { 2 => 'Time' },
2181        Shift => 'Time',
2182        Avoid => 1,
2183        # handle values in the form "2010-02-12T13:27:14-0800"
2184        ValueConv => q{
2185            require Image::ExifTool::XMP;
2186            $val =  Image::ExifTool::XMP::ConvertXMPDate($val);
2187            $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
2188            return $val;
2189        },
2190        ValueConvInv => q{
2191            require Image::ExifTool::XMP;
2192            my $tmp = Image::ExifTool::XMP::FormatXMPDate($val);
2193            ($val = $tmp) =~ s/([-+]\d{2}):(\d{2})$/$1$2/ if defined $tmp; # remove time zone colon
2194            return $val;
2195        },
2196        PrintConv => '$self->ConvertDateTime($val)',
2197        PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
2198    },
2199    '@xyz' => { #PH (iPhone 3GS)
2200        Name => 'GPSCoordinates',
2201        Groups => { 2 => 'Location' },
2202        Avoid => 1,
2203        ValueConv => \&ConvertISO6709,
2204        ValueConvInv => \&ConvInvISO6709,
2205        PrintConv => \&PrintGPSCoordinates,
2206        PrintConvInv => \&PrintInvGPSCoordinates,
2207    },
2208    # RDT1 - pairs of int32u_BE, starting at byte 8: "458275 471846"
2209    # RDT2 - pairs of int32u_BE, starting at byte 8: "472276 468526"
2210    # RDT3 - pairs of int32u_BE, starting at byte 8: "876603 482191"
2211    # RDT4 - pairs of int32u_BE, starting at byte 8: "1955 484612"
2212    # RDT6 - empty
2213    # RDT7 - empty
2214    # RDT8 - empty
2215    # RDT9 - only 16-byte header?
2216    # the boxes below all have a similar header (little-endian):
2217    #  0 int32u - number of records
2218    #  4 ? - "1e 00"
2219    #  6 int16u - record length in bytes
2220    #  8 ? - "23 01 00 00 00 00 00 00"
2221    #  16 - start of records (each record ends in an int64u timestamp in ns)
2222    # RDTA - float[4],ts: "-0.31289672 -0.2245330 11.303817 0 775.780"
2223    # RDTB - float[4],ts: "-0.04841613 -0.2166595 0.0724792 0 775.780"
2224    # RDTC - float[4],ts: "27.60925 -27.10037 -13.27285 0 775.829"
2225    # RDTD - int16s[3],ts: "353 -914 16354 0 775.829"
2226    # RDTG - ts: "775.825"
2227    # RDTI - float[4],ts: "0.00165951 0.005770059 0.06838259 0.1744695 775.862"
2228    # ---- Samsung ----
2229    vndr => 'Vendor', #PH (Samsung PL70)
2230    SDLN => 'PlayMode', #PH (NC, Samsung ST80 "SEQ_PLAY")
2231    INFO => {
2232        Name => 'SamsungINFO',
2233        SubDirectory => { TagTable => 'Image::ExifTool::Samsung::INFO' },
2234    },
2235   '@sec' => { #PH (Samsung WB30F)
2236        Name => 'SamsungSec',
2237        SubDirectory => { TagTable => 'Image::ExifTool::Samsung::sec' },
2238    },
2239    'smta' => { #PH (Samsung SM-C101)
2240        Name => 'SamsungSmta',
2241        SubDirectory => {
2242            TagTable => 'Image::ExifTool::Samsung::smta',
2243            Start => 4,
2244        },
2245    },
2246    cver => 'CodeVersion', #PH (guess, Samsung MV900F)
2247    # ducp - 4 bytes all zero (Samsung ST96,WB750), 52 bytes all zero (Samsung WB30F)
2248    # edli - 52 bytes all zero (Samsung WB30F)
2249    # @etc - 4 bytes all zero (Samsung WB30F)
2250    # saut - 4 bytes all zero (Samsung SM-N900T)
2251    # smrd - string "TRUEBLUE" (Samsung SM-C101)
2252    # ---- TomTom Bandit Action Cam ----
2253    TTMD => {
2254        Name => 'TomTomMetaData',
2255        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TomTom' },
2256    },
2257    # ---- Samsung Gear 360 ----
2258    vrot => {
2259        Name => 'AccelerometerData',
2260        Notes => q{
2261            accelerometer readings for each frame of the video, expressed as sets of
2262            yaw, pitch and roll angles in degrees
2263        },
2264        Format => 'rational64s',
2265        ValueConv => '$val =~ s/^-?\d+ //; \$val', # (ignore leading version/size words)
2266    },
2267    # m360 - 8 bytes "0 0 0 0 0 0 0 1"
2268    # opax - 164 bytes unknown (center and affine arrays? ref 26)
2269    # opai - 32 bytes (maybe contains a serial number starting at byte 16? - PH) (rgb gains, degamma, gamma? ref 26)
2270    # intv - 16 bytes all zero
2271    # ---- Xaiomi ----
2272    mcvr => {
2273        Name => 'PreviewImage',
2274        Groups => { 2 => 'Preview' },
2275        Binary => 1,
2276    },
2277    # ---- Unknown ----
2278    # CDET - 128 bytes (unknown origin)
2279    # mtyp - 4 bytes all zero (some drone video)
2280    # kgrf - 8 bytes all zero ? (in udta inside trak atom)
2281    # kgcg - 128 bytes 0's and 1's
2282    # kgsi - 4 bytes "00 00 00 80"
2283    # FIEL - 18 bytes "FIEL\0\x01\0\0\0..."
2284#
2285# other 3rd-party tags
2286# (ref http://code.google.com/p/mp4parser/source/browse/trunk/isoparser/src/main/resources/isoparser-default.properties?r=814)
2287#
2288    ccid => 'ContentID',
2289    icnu => 'IconURI',
2290    infu => 'InfoURL',
2291    cdis => 'ContentDistributorID',
2292    albr => { Name => 'AlbumArtist', Groups => { 2 => 'Author' } },
2293    cvru => 'CoverURI',
2294    lrcu => 'LyricsURI',
2295
2296    tags => {   # found in Audible .m4b audio books (ref PH)
2297        Name => 'Audible_tags',
2298        SubDirectory => { TagTable => 'Image::ExifTool::Audible::tags' },
2299    },
2300);
2301
2302# Unknown information stored in HTC One (M8) videos - PH
2303%Image::ExifTool::QuickTime::HTCBinary = (
2304    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2305    GROUPS => { 0 => 'MakerNotes', 1 => 'HTC', 2 => 'Video' },
2306    TAG_PREFIX => 'HTCBinary',
2307    FORMAT => 'int32u',
2308    FIRST_ENTRY => 0,
2309    # 0 - values: 1
2310    # 1 - values: 0
2311    # 2 - values: 0
2312    # 3 - values: FileSize minus 12 (why?)
2313    # 4 - values: 12
2314);
2315
2316# TomTom Bandit Action Cam metadata (ref PH)
2317%Image::ExifTool::QuickTime::TomTom = (
2318    PROCESS_PROC => \&ProcessMOV,
2319    GROUPS => { 2 => 'Video' },
2320    NOTES => 'Tags found in TomTom Bandit Action Cam MP4 videos.',
2321    TTAD => {
2322        Name => 'TomTomAD',
2323        SubDirectory => {
2324            TagTable => 'Image::ExifTool::QuickTime::Stream',
2325            ProcessProc => \&Image::ExifTool::QuickTime::ProcessTTAD,
2326        },
2327    },
2328    TTHL => { Name => 'TomTomHL', Binary => 1, Unknown => 1 }, # (mostly zeros)
2329    # (TTID values are different for each video)
2330    TTID => { Name => 'TomTomID', ValueConv => 'unpack("x4H*",$val)' },
2331    TTVI => { Name => 'TomTomVI', Format => 'int32u', Unknown => 1 }, # seen: "0 1 61 508 508"
2332    # TTVD seen: "normal 720p 60fps 60fps 16/9 wide 1x"
2333    TTVD => { Name => 'TomTomVD', ValueConv => 'my @a = ($val =~ /[\x20-\x7f]+/g); "@a"' },
2334);
2335
2336# User-specific media data atoms (ref 11)
2337%Image::ExifTool::QuickTime::UserMedia = (
2338    PROCESS_PROC => \&ProcessMOV,
2339    GROUPS => { 2 => 'Video' },
2340    MTDT => {
2341        Name => 'MetaData',
2342        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MetaData' },
2343    },
2344);
2345
2346# User-specific media data atoms (ref 11)
2347%Image::ExifTool::QuickTime::MetaData = (
2348    PROCESS_PROC => \&ProcessMetaData,
2349    GROUPS => { 2 => 'Video' },
2350    TAG_PREFIX => 'MetaData',
2351    0x01 => 'Title',
2352    0x03 => {
2353        Name => 'ProductionDate',
2354        Groups => { 2 => 'Time' },
2355        Shift => 'Time',
2356        Writable => 1,
2357        Permanent => 1,
2358        DelValue => '0000/00/00 00:00:00',
2359        # translate from format "YYYY/mm/dd HH:MM:SS"
2360        ValueConv => '$val=~tr{/}{:}; $val',
2361        ValueConvInv => '$val=~s[^(\d{4}):(\d{2}):][$1/$2/]; $val',
2362        PrintConv => '$self->ConvertDateTime($val)',
2363        PrintConvInv => '$self->InverseDateTime($val)',
2364    },
2365    0x04 => 'Software',
2366    0x05 => 'Product',
2367    0x0a => {
2368        Name => 'TrackProperty',
2369        RawConv => 'my @a=unpack("Nnn",$val); "@a"',
2370        PrintConv => [
2371            { 0 => 'No presentation', BITMASK => { 0 => 'Main track' } },
2372            { 0 => 'No attributes',   BITMASK => { 15 => 'Read only' } },
2373            '"Priority $val"',
2374        ],
2375    },
2376    0x0b => {
2377        Name => 'TimeZone',
2378        Groups => { 2 => 'Time' },
2379        Writable => 1,
2380        Permanent => 1,
2381        DelValue => 0,
2382        RawConv => 'Get16s(\$val,0)',
2383        RawConvInv => 'Set16s($val)',
2384        PrintConv => 'TimeZoneString($val)',
2385        PrintConvInv => q{
2386            return undef unless $val =~ /^([-+])(\d{1,2}):?(\d{2})$/'
2387            my $tzmin = $2 * 60 + $3;
2388            $tzmin = -$tzmin if $1 eq '-';
2389            return $tzmin;
2390        }
2391    },
2392    0x0c => {
2393        Name => 'ModifyDate',
2394        Groups => { 2 => 'Time' },
2395        Shift => 'Time',
2396        Writable => 1,
2397        Permanent => 1,
2398        DelValue => '0000/00/00 00:00:00',
2399        # translate from format "YYYY/mm/dd HH:MM:SS"
2400        ValueConv => '$val=~tr{/}{:}; $val',
2401        ValueConvInv => '$val=~s[^(\d{4}):(\d{2}):][$1/$2/]; $val',
2402        PrintConv => '$self->ConvertDateTime($val)',
2403        PrintConvInv => '$self->InverseDateTime($val)',
2404    },
2405);
2406
2407# compressed movie atoms (ref http://wiki.multimedia.cx/index.php?title=QuickTime_container#cmov)
2408%Image::ExifTool::QuickTime::CMovie = (
2409    PROCESS_PROC => \&ProcessMOV,
2410    GROUPS => { 2 => 'Video' },
2411    dcom => 'Compression',
2412    # cmvd - compressed moov atom data
2413);
2414
2415# Profile atoms (ref 11)
2416%Image::ExifTool::QuickTime::Profile = (
2417    PROCESS_PROC => \&ProcessMOV,
2418    GROUPS => { 2 => 'Video' },
2419    FPRF => {
2420        Name => 'FileGlobalProfile',
2421        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::FileProf' },
2422    },
2423    APRF => {
2424        Name => 'AudioProfile',
2425        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AudioProf' },
2426    },
2427    VPRF => {
2428        Name => 'VideoProfile',
2429        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::VideoProf' },
2430    },
2431    OLYM => { #PH
2432        Name => 'OlympusOLYM',
2433        SubDirectory => {
2434            TagTable => 'Image::ExifTool::Olympus::OLYM',
2435            ByteOrder => 'BigEndian',
2436        },
2437    },
2438);
2439
2440# FPRF atom information (ref 11)
2441%Image::ExifTool::QuickTime::FileProf = (
2442    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2443    GROUPS => { 2 => 'Video' },
2444    FORMAT => 'int32u',
2445    0 => { Name => 'FileProfileVersion', Unknown => 1 }, # unknown = uninteresting
2446    1 => {
2447        Name => 'FileFunctionFlags',
2448        PrintConv => { BITMASK => {
2449            28 => 'Fragmented',
2450            29 => 'Additional tracks',
2451            30 => 'Edited', # (main AV track is edited)
2452        }},
2453    },
2454    # 2 - reserved
2455);
2456
2457# APRF atom information (ref 11)
2458%Image::ExifTool::QuickTime::AudioProf = (
2459    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2460    GROUPS => { 2 => 'Audio' },
2461    FORMAT => 'int32u',
2462    0 => { Name => 'AudioProfileVersion', Unknown => 1 },
2463    1 => 'AudioTrackID',
2464    2 => {
2465        Name => 'AudioCodec',
2466        Format => 'undef[4]',
2467    },
2468    3 => {
2469        Name => 'AudioCodecInfo',
2470        Unknown => 1,
2471        PrintConv => 'sprintf("0x%.4x", $val)',
2472    },
2473    4 => {
2474        Name => 'AudioAttributes',
2475        PrintConv => { BITMASK => {
2476            0 => 'Encrypted',
2477            1 => 'Variable bitrate',
2478            2 => 'Dual mono',
2479        }},
2480    },
2481    5 => {
2482        Name => 'AudioAvgBitrate',
2483        ValueConv => '$val * 1000',
2484        PrintConv => 'ConvertBitrate($val)',
2485    },
2486    6 => {
2487        Name => 'AudioMaxBitrate',
2488        ValueConv => '$val * 1000',
2489        PrintConv => 'ConvertBitrate($val)',
2490    },
2491    7 => 'AudioSampleRate',
2492    8 => 'AudioChannels',
2493);
2494
2495# VPRF atom information (ref 11)
2496%Image::ExifTool::QuickTime::VideoProf = (
2497    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2498    GROUPS => { 2 => 'Video' },
2499    FORMAT => 'int32u',
2500    0 => { Name => 'VideoProfileVersion', Unknown => 1 },
2501    1 => 'VideoTrackID',
2502    2 => {
2503        Name => 'VideoCodec',
2504        Format => 'undef[4]',
2505    },
2506    3 => {
2507        Name => 'VideoCodecInfo',
2508        Unknown => 1,
2509        PrintConv => 'sprintf("0x%.4x", $val)',
2510    },
2511    4 => {
2512        Name => 'VideoAttributes',
2513        PrintConv => { BITMASK => {
2514            0 => 'Encrypted',
2515            1 => 'Variable bitrate',
2516            2 => 'Variable frame rate',
2517            3 => 'Interlaced',
2518        }},
2519    },
2520    5 => {
2521        Name => 'VideoAvgBitrate',
2522        ValueConv => '$val * 1000',
2523        PrintConv => 'ConvertBitrate($val)',
2524    },
2525    6 => {
2526        Name => 'VideoMaxBitrate',
2527        ValueConv => '$val * 1000',
2528        PrintConv => 'ConvertBitrate($val)',
2529    },
2530    7 => {
2531        Name => 'VideoAvgFrameRate',
2532        Format => 'fixed32u',
2533        PrintConv => 'int($val * 1000 + 0.5) / 1000',
2534    },
2535    8 => {
2536        Name => 'VideoMaxFrameRate',
2537        Format => 'fixed32u',
2538        PrintConv => 'int($val * 1000 + 0.5) / 1000',
2539    },
2540    9 => {
2541        Name => 'VideoSize',
2542        Format => 'int16u[2]',
2543        PrintConv => '$val=~tr/ /x/; $val',
2544    },
2545    10 => {
2546        Name => 'PixelAspectRatio',
2547        Format => 'int16u[2]',
2548        PrintConv => '$val=~tr/ /:/; $val',
2549    },
2550);
2551
2552# meta atoms
2553%Image::ExifTool::QuickTime::Meta = (
2554    PROCESS_PROC => \&ProcessMOV,
2555    WRITE_PROC => \&WriteQuickTime,
2556    GROUPS => { 1 => 'Meta', 2 => 'Video' },
2557    ilst => {
2558        Name => 'ItemList',
2559        SubDirectory => {
2560            TagTable => 'Image::ExifTool::QuickTime::ItemList',
2561            HasData => 1, # process atoms as containers with 'data' elements
2562        },
2563    },
2564    # MP4 tags (ref 5)
2565    hdlr => {
2566        Name => 'Handler',
2567        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Handler' },
2568    },
2569    dinf => {
2570        Name => 'DataInfo', # (don't change this name -- used to recognize directory when writing)
2571        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::DataInfo' },
2572    },
2573    ipmc => {
2574        Name => 'IPMPControl',
2575        Flags => ['Binary','Unknown'],
2576    },
2577    iloc => {
2578        Name => 'ItemLocation',
2579        RawConv => \&ParseItemLocation,
2580        WriteHook => \&ParseItemLocation,
2581        Notes => 'parsed, but not extracted as a tag',
2582    },
2583    ipro => {
2584        Name => 'ItemProtection',
2585        Flags => ['Binary','Unknown'],
2586    },
2587    iinf => [{
2588        Name => 'ItemInformation',
2589        Condition => '$$valPt =~ /^\0/', # (check for version 0)
2590        SubDirectory => {
2591            TagTable => 'Image::ExifTool::QuickTime::ItemInfo',
2592            Start => 6, # (4-byte version/flags + 2-byte count)
2593        },
2594    },{
2595        Name => 'ItemInformation',
2596        SubDirectory => {
2597            TagTable => 'Image::ExifTool::QuickTime::ItemInfo',
2598            Start => 8, # (4-byte version/flags + 4-byte count)
2599        },
2600    }],
2601   'xml ' => {
2602        Name => 'XML',
2603        Flags => [ 'Binary', 'Protected' ],
2604        SubDirectory => {
2605            TagTable => 'Image::ExifTool::XMP::XML',
2606            IgnoreProp => { NonRealTimeMeta => 1 }, # ignore container for Sony 'nrtm'
2607        },
2608    },
2609   'keys' => {
2610        Name => 'Keys',
2611        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Keys' },
2612    },
2613    bxml => {
2614        Name => 'BinaryXML',
2615        Flags => ['Binary','Unknown'],
2616    },
2617    pitm => [{
2618        Name => 'PrimaryItemReference',
2619        Condition => '$$valPt =~ /^\0/', # (version 0?)
2620        RawConv => '$$self{PrimaryItem} = unpack("x4n",$val)',
2621        WriteHook => sub { my ($val,$et) = @_; $$et{PrimaryItem} = unpack("x4n",$val); },
2622    },{
2623        Name => 'PrimaryItemReference',
2624        RawConv => '$$self{PrimaryItem} = unpack("x4N",$val)',
2625        WriteHook => sub { my ($val,$et) = @_; $$et{PrimaryItem} = unpack("x4N",$val); },
2626    }],
2627    free => { #PH
2628        Name => 'Free',
2629        Flags => ['Binary','Unknown'],
2630    },
2631    iprp => {
2632        Name => 'ItemProperties',
2633        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ItemProp' },
2634    },
2635    iref => {
2636        Name => 'ItemReference',
2637        # the version is needed to parse some of the item references
2638        Condition => '$$self{ItemRefVersion} = ord($$valPt); 1',
2639        SubDirectory => {
2640            TagTable => 'Image::ExifTool::QuickTime::ItemRef',
2641            Start => 4,
2642        },
2643    },
2644    idat => {
2645        Name => 'MetaImageSize', #PH (NC)
2646        Format => 'int16u',
2647        # (don't know what the first two numbers are for)
2648        PrintConv => '$val =~ s/^(\d+) (\d+) (\d+) (\d+)/${3}x$4/; $val',
2649    },
2650    uuid => [
2651        { #PH (Canon R5/R6 HIF)
2652            Name => 'MetaVersion', # (NC)
2653            Condition => '$$valPt=~/^\x85\xc0\xb6\x87\x82\x0f\x11\xe0\x81\x11\xf4\xce\x46\x2b\x6a\x48/',
2654            RawConv => 'substr($val, 0x14)',
2655        },
2656        {
2657            Name => 'UUID-Unknown',
2658            %unknownInfo,
2659        },
2660    ],
2661);
2662
2663# additional metadata container (ref ISO14496-12:2015)
2664%Image::ExifTool::QuickTime::OtherMeta = (
2665    PROCESS_PROC => \&ProcessMOV,
2666    WRITE_PROC => \&WriteQuickTime,
2667    GROUPS => { 2 => 'Video' },
2668    mere => {
2669        Name => 'MetaRelation',
2670        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MetaRelation' },
2671    },
2672    meta => {
2673        Name => 'Meta',
2674        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
2675    },
2676);
2677
2678# metabox relation (ref ISO14496-12:2015)
2679%Image::ExifTool::QuickTime::MetaRelation = (
2680    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2681    GROUPS => { 2 => 'Video' },
2682    FORMAT => 'int32u',
2683    # 0 => 'MetaRelationVersion',
2684    # 1 => 'FirstMetaboxHandlerType',
2685    # 2 => 'FirstMetaboxHandlerType',
2686    # 3 => { Name => 'MetaboxRelation', Format => 'int8u' },
2687);
2688
2689%Image::ExifTool::QuickTime::ItemProp = (
2690    PROCESS_PROC => \&ProcessMOV,
2691    WRITE_PROC => \&WriteQuickTime,
2692    GROUPS => { 2 => 'Image' },
2693    ipco => {
2694        Name => 'ItemPropertyContainer',
2695        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ItemPropCont' },
2696    },
2697    ipma => {
2698        Name => 'ItemPropertyAssociation',
2699        RawConv => \&ParseItemPropAssoc,
2700        WriteHook => \&ParseItemPropAssoc,
2701        Notes => 'parsed, but not extracted as a tag',
2702    },
2703);
2704
2705%Image::ExifTool::QuickTime::ItemPropCont = (
2706    PROCESS_PROC => \&ProcessMOV,
2707    WRITE_PROC => \&WriteQuickTime,
2708    PERMANENT => 1, # (can't be deleted)
2709    GROUPS => { 2 => 'Image' },
2710    VARS => { START_INDEX => 1 },   # show verbose indices starting at 1
2711    colr => [{
2712        Name => 'ICC_Profile',
2713        Condition => '$$valPt =~ /^(prof|rICC)/',
2714        Permanent => 0, # (in QuickTime, this writes a zero-length box instead of deleting)
2715        SubDirectory => {
2716            TagTable => 'Image::ExifTool::ICC_Profile::Main',
2717            Start => 4,
2718        },
2719    },{
2720        Name => 'ColorRepresentation',
2721        ValueConv => 'join(" ", substr($val,0,4), unpack("x4n*",$val))',
2722    }],
2723    irot => {
2724        Name => 'Rotation',
2725        Format => 'int8u',
2726        Writable => 'int8u',
2727        Protected => 1,
2728        ValueConv => '$val * 90',
2729        ValueConvInv => 'int($val / 90 + 0.5)',
2730    },
2731    ispe => {
2732        Name => 'ImageSpatialExtent',
2733        Condition => '$$valPt =~ /^\0{4}/',     # (version/flags == 0/0)
2734        RawConv => q{
2735            my @dim = unpack("x4N*", $val);
2736            return undef if @dim < 2;
2737            unless ($$self{DOC_NUM}) {
2738                $self->FoundTag(ImageWidth => $dim[0]);
2739                $self->FoundTag(ImageHeight => $dim[1]);
2740            }
2741            return join ' ', @dim;
2742        },
2743        PrintConv => '$val =~ tr/ /x/; $val',
2744    },
2745    pixi => {
2746        Name => 'ImagePixelDepth',
2747        Condition => '$$valPt =~ /^\0{4}./s',   # (version/flags == 0/0 and count)
2748        RawConv => 'join " ", unpack("x5C*", $val)',
2749    },
2750    auxC => {
2751        Name => 'AuxiliaryImageType',
2752        Format => 'undef',
2753        RawConv => '$val = substr($val, 4); $val =~ s/\0.*//s; $val',
2754    },
2755    pasp => {
2756        Name => 'PixelAspectRatio',
2757        Format => 'int32u',
2758        Writable => 'int32u',
2759        Protected => 1,
2760    },
2761    rloc => {
2762        Name => 'RelativeLocation',
2763        Format => 'int32u',
2764        RawConv => '$val =~ s/^\S+\s+//; $val', # remove version/flags
2765    },
2766    clap => {
2767        Name => 'CleanAperture',
2768        Format => 'rational64s',
2769        Notes => '4 numbers: width, height, left and top',
2770    },
2771    hvcC => {
2772        Name => 'HEVCConfiguration',
2773        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HEVCConfig' },
2774    },
2775    av1C => {
2776        Name => 'AV1Configuration',
2777        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AV1Config' },
2778    },
2779);
2780
2781# HEVC configuration (ref https://github.com/MPEGGroup/isobmff/blob/master/IsoLib/libisomediafile/src/HEVCConfigAtom.c)
2782%Image::ExifTool::QuickTime::HEVCConfig = (
2783    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2784    GROUPS => { 2 => 'Video' },
2785    FIRST_ENTRY => 0,
2786    0 => 'HEVCConfigurationVersion',
2787    1 => {
2788        Name => 'GeneralProfileSpace',
2789        Mask => 0xc0,
2790        PrintConv => { 0 => 'Conforming' },
2791    },
2792    1.1 => {
2793        Name => 'GeneralTierFlag',
2794        Mask => 0x20,
2795        PrintConv => {
2796            0 => 'Main Tier',
2797            1 => 'High Tier',
2798        },
2799    },
2800    1.2 => {
2801        Name => 'GeneralProfileIDC',
2802        Mask => 0x1f,
2803        PrintConv => {
2804            0 => 'No Profile',
2805            1 => 'Main',
2806            2 => 'Main 10',
2807            3 => 'Main Still Picture',
2808            4 => 'Format Range Extensions',
2809            5 => 'High Throughput',
2810            6 => 'Multiview Main',
2811            7 => 'Scalable Main',
2812            8 => '3D Main',
2813            9 => 'Screen Content Coding Extensions',
2814            10 => 'Scalable Format Range Extensions',
2815            11 => 'High Throughput Screen Content Coding Extensions',
2816        },
2817    },
2818    2 => {
2819        Name => 'GenProfileCompatibilityFlags',
2820        Format => 'int32u',
2821        PrintConv => { BITMASK => {
2822            31 => 'No Profile',             # (bit 0 in stream)
2823            30 => 'Main',                   # (bit 1 in stream)
2824            29 => 'Main 10',                # (bit 2 in stream)
2825            28 => 'Main Still Picture',     # (bit 3 in stream)
2826            27 => 'Format Range Extensions',# (...)
2827            26 => 'High Throughput',
2828            25 => 'Multiview Main',
2829            24 => 'Scalable Main',
2830            23 => '3D Main',
2831            22 => 'Screen Content Coding Extensions',
2832            21 => 'Scalable Format Range Extensions',
2833            20 => 'High Throughput Screen Content Coding Extensions',
2834        }},
2835    },
2836    6 => {
2837        Name => 'ConstraintIndicatorFlags',
2838        Format => 'int8u[6]',
2839    },
2840    12 => {
2841        Name => 'GeneralLevelIDC',
2842        PrintConv => 'sprintf("%d (level %.1f)", $val, $val/30)',
2843    },
2844    13 => {
2845        Name => 'MinSpatialSegmentationIDC',
2846        Format => 'int16u',
2847        Mask => 0x0fff,
2848    },
2849    15 => {
2850        Name => 'ParallelismType',
2851        Mask => 0x03,
2852    },
2853    16 => {
2854        Name => 'ChromaFormat',
2855        Mask => 0x03,
2856        PrintConv => {
2857            0 => 'Monochrome',
2858            1 => '4:2:0',
2859            2 => '4:2:2',
2860            3 => '4:4:4',
2861        },
2862    },
2863    17 => {
2864        Name => 'BitDepthLuma',
2865        Mask => 0x07,
2866        ValueConv => '$val + 8',
2867    },
2868    18 => {
2869        Name => 'BitDepthChroma',
2870        Mask => 0x07,
2871        ValueConv => '$val + 8',
2872    },
2873    19 => {
2874        Name => 'AverageFrameRate',
2875        Format => 'int16u',
2876        ValueConv => '$val / 256',
2877    },
2878    21 => {
2879        Name => 'ConstantFrameRate',
2880        Mask => 0xc0,
2881        PrintConv => {
2882            0 => 'Unknown',
2883            1 => 'Constant Frame Rate',
2884            2 => 'Each Temporal Layer is Constant Frame Rate',
2885        },
2886    },
2887    21.1 => {
2888        Name => 'NumTemporalLayers',
2889        Mask => 0x38,
2890    },
2891    21.2 => {
2892        Name => 'TemporalIDNested',
2893        Mask => 0x04,
2894        PrintConv => { 0 => 'No', 1 => 'Yes' },
2895    },
2896    #21.3 => {
2897    #    Name => 'NALUnitLengthSize',
2898    #    Mask => 0x03,
2899    #    ValueConv => '$val + 1',
2900    #    PrintConv => { 1 => '8-bit', 2 => '16-bit', 4 => '32-bit' },
2901    #},
2902    #22 => 'NumberOfNALUnitArrays',
2903    # (don't decode the NAL unit arrays)
2904);
2905
2906# HEVC configuration (ref https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox)
2907%Image::ExifTool::QuickTime::AV1Config = (
2908    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2909    GROUPS => { 2 => 'Video' },
2910    FIRST_ENTRY => 0,
2911    0 => {
2912        Name => 'AV1ConfigurationVersion',
2913        Mask => 0x7f,
2914    },
2915    1.0 => {
2916        Name => 'SeqProfile',
2917        Mask => 0xe0,
2918        Unknown => 1,
2919    },
2920    1.1 => {
2921        Name => 'SeqLevelIdx0',
2922        Mask => 0x1f,
2923        Unknown => 1,
2924    },
2925    2.0 => {
2926        Name => 'SeqTier0',
2927        Mask => 0x80,
2928        Unknown => 1,
2929    },
2930    2.1 => {
2931        Name => 'HighBitDepth',
2932        Mask => 0x40,
2933        Unknown => 1,
2934    },
2935    2.2 => {
2936        Name => 'TwelveBit',
2937        Mask => 0x20,
2938        Unknown => 1,
2939    },
2940    2.3 => {
2941        Name => 'ChromaFormat', # (Monochrome+SubSamplingX+SubSamplingY)
2942        Notes => 'bits: 0x04 = Monochrome, 0x02 = SubSamplingX, 0x01 = SubSamplingY',
2943        Mask => 0x1c,
2944        PrintConv => {
2945            0x00 => 'YUV 4:4:4',
2946            0x02 => 'YUV 4:2:2',
2947            0x03 => 'YUV 4:2:0',
2948            0x07 => 'Monochrome 4:0:0',
2949        },
2950    },
2951    2.4 => {
2952        Name => 'ChromaSamplePosition',
2953        Mask => 0x03,
2954        PrintConv => {
2955            0 => 'Unknown',
2956            1 => 'Vertical',
2957            2 => 'Colocated',
2958            3 => '(reserved)',
2959        },
2960    },
2961    3 => {
2962        Name => 'InitialDelaySamples',
2963        RawConv => '$val & 0x10 ? undef : ($val & 0x0f) + 1',
2964        Unknown => 1,
2965    },
2966);
2967
2968%Image::ExifTool::QuickTime::ItemRef = (
2969    PROCESS_PROC => \&ProcessMOV,
2970    WRITE_PROC => \&WriteQuickTime,
2971    GROUPS => { 2 => 'Image' },
2972    # (Note: ExifTool's ItemRefVersion may be used to test the iref version number)
2973    NOTES => q{
2974        The Item reference entries listed in the table below contain information about
2975        the associations between items in the file.  This information is used by
2976        ExifTool, but these entries are not extracted as tags.
2977    },
2978    dimg => { Name => 'DerivedImageRef',   RawConv => 'undef' },
2979    thmb => { Name => 'ThumbnailRef',      RawConv => 'undef' },
2980    auxl => { Name => 'AuxiliaryImageRef', RawConv => 'undef' },
2981    cdsc => {
2982        Name => 'ContentDescribes',
2983        RawConv => \&ParseContentDescribes,
2984        WriteHook => \&ParseContentDescribes,
2985    },
2986);
2987
2988%Image::ExifTool::QuickTime::ItemInfo = (
2989    PROCESS_PROC => \&ProcessMOV,
2990    WRITE_PROC => \&WriteQuickTime,
2991    GROUPS => { 2 => 'Image' },
2992    # avc1 - AVC image
2993    # hvc1 - HEVC image
2994    # lhv1 - L-HEVC image
2995    # infe - ItemInformationEntry
2996    # infe types: avc1,hvc1,lhv1,Exif,xml1,iovl(overlay image),grid,mime,hvt1(tile image)
2997    infe => {
2998        Name => 'ItemInfoEntry',
2999        RawConv => \&ParseItemInfoEntry,
3000        WriteHook => \&ParseItemInfoEntry,
3001        Notes => 'parsed, but not extracted as a tag',
3002    },
3003);
3004
3005# track reference atoms
3006%Image::ExifTool::QuickTime::TrackRef = (
3007    PROCESS_PROC => \&ProcessMOV,
3008    GROUPS => { 1 => 'Track#', 2 => 'Video' },
3009    chap => { Name => 'ChapterListTrackID', Format => 'int32u' },
3010    tmcd => { Name => 'TimeCode', Format => 'int32u' },
3011    mpod => { #PH (FLIR MP4)
3012        Name => 'ElementaryStreamTrack',
3013        Format => 'int32u',
3014        ValueConv => '$val =~ s/^1 //; $val',  # (why 2 numbers? -- ignore the first if "1")
3015    },
3016    # also: sync, scpt, ssrc, iTunesInfo
3017    cdsc => {
3018        Name => 'ContentDescribes',
3019        Format => 'int32u',
3020        PrintConv => '"Track $val"',
3021    },
3022    # cdep (Structural Dependency QT tag?)
3023);
3024
3025# track aperture mode dimensions atoms
3026# (ref https://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap2/qtff2.html)
3027%Image::ExifTool::QuickTime::TrackAperture = (
3028    PROCESS_PROC => \&ProcessMOV,
3029    GROUPS => { 1 => 'Track#', 2 => 'Video' },
3030    clef => {
3031        Name => 'CleanApertureDimensions',
3032        Format => 'fixed32u',
3033        Count => 3,
3034        ValueConv => '$val =~ s/^.*? //; $val', # remove flags word
3035        PrintConv => '$val =~ tr/ /x/; $val',
3036    },
3037    prof => {
3038        Name => 'ProductionApertureDimensions',
3039        Format => 'fixed32u',
3040        Count => 3,
3041        ValueConv => '$val =~ s/^.*? //; $val',
3042        PrintConv => '$val =~ tr/ /x/; $val',
3043    },
3044    enof => {
3045        Name => 'EncodedPixelsDimensions',
3046        Format => 'fixed32u',
3047        Count => 3,
3048        ValueConv => '$val =~ s/^.*? //; $val',
3049        PrintConv => '$val =~ tr/ /x/; $val',
3050    },
3051);
3052
3053# item list atoms
3054# -> these atoms are unique, and contain one or more 'data' atoms
3055%Image::ExifTool::QuickTime::ItemList = (
3056    PROCESS_PROC => \&ProcessMOV,
3057    WRITE_PROC => \&WriteQuickTime,
3058    CHECK_PROC => \&CheckQTValue,
3059    WRITABLE => 1,
3060    PREFERRED => 2, # (preferred over UserData and Keys tags when writing)
3061    FORMAT => 'string',
3062    GROUPS => { 1 => 'ItemList', 2 => 'Audio' },
3063    WRITE_GROUP => 'ItemList',
3064    LANG_INFO => \&GetLangInfo,
3065    NOTES => q{
3066        This is the preferred location for creating new QuickTime tags.  Tags in
3067        this table support alternate languages which are accessed by adding a
3068        3-character ISO 639-2 language code and an optional ISO 3166-1 alpha 2
3069        country code to the tag name (eg. "ItemList:Title-fra" or
3070        "ItemList::Title-fra-FR").  When creating a new Meta box to contain the
3071        ItemList directory, by default ExifTool adds an 'mdir' (Metadata) Handler
3072        box because Apple software may ignore ItemList tags otherwise, but the API
3073        L<QuickTimeHandler|../ExifTool.html#QuickTimeHandler> option may be set to 0 to avoid this.
3074    },
3075    # in this table, binary 1 and 2-byte "data"-type tags are interpreted as
3076    # int8u and int16u.  Multi-byte binary "data" tags are extracted as binary data.
3077    # (Note that the Preferred property is set to 0 for some tags to prevent them
3078    #  from being created when a same-named tag already exists in the table)
3079    "\xa9ART" => 'Artist',
3080    "\xa9alb" => 'Album',
3081    "\xa9aut" => { Name => 'Author', Avoid => 1, Groups => { 2 => 'Author' } }, #forum10091 ('auth' is preferred)
3082    "\xa9cmt" => 'Comment',
3083    "\xa9com" => { Name => 'Composer', Avoid => 1, }, # ("\xa9wrt" is preferred in ItemList)
3084    "\xa9day" => {
3085        Name => 'ContentCreateDate',
3086        Groups => { 2 => 'Time' },
3087        Shift => 'Time',
3088        # handle values in the form "2010-02-12T13:27:14-0800"
3089        ValueConv => q{
3090            require Image::ExifTool::XMP;
3091            $val =  Image::ExifTool::XMP::ConvertXMPDate($val);
3092            $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
3093            return $val;
3094        },
3095        ValueConvInv => q{
3096            require Image::ExifTool::XMP;
3097            $val =  Image::ExifTool::XMP::FormatXMPDate($val);
3098            $val =~ s/([-+]\d{2}):(\d{2})$/$1$2/; # remove time zone colon
3099            return $val;
3100        },
3101        PrintConv => '$self->ConvertDateTime($val)',
3102        PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
3103    },
3104    "\xa9des" => 'Description', #4
3105    "\xa9enc" => 'EncodedBy', #10
3106    "\xa9gen" => 'Genre',
3107    "\xa9grp" => 'Grouping',
3108    "\xa9lyr" => 'Lyrics',
3109    "\xa9nam" => 'Title',
3110    "\xa9too" => 'Encoder',
3111    "\xa9trk" => 'Track',
3112    "\xa9wrt" => 'Composer',
3113#
3114# the following tags written by AtomicParsley 0.9.6
3115# (ref https://exiftool.org/forum/index.php?topic=11455.0)
3116#
3117    "\xa9st3" => 'Subtitle',
3118    "\xa9con" => 'Conductor',
3119    "\xa9sol" => 'Soloist',
3120    "\xa9arg" => 'Arranger',
3121    "\xa9ope" => 'OriginalArtist',
3122    "\xa9dir" => 'Director',
3123    "\xa9ard" => 'ArtDirector',
3124    "\xa9sne" => 'SoundEngineer',
3125    "\xa9prd" => 'Producer',
3126    "\xa9xpd" => 'ExecutiveProducer',
3127    sdes      => 'StoreDescription',
3128#
3129    '----' => {
3130        Name => 'iTunesInfo',
3131        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::iTunesInfo' },
3132    },
3133    aART => { Name => 'AlbumArtist', Groups => { 2 => 'Author' } },
3134    covr => { Name => 'CoverArt',    Groups => { 2 => 'Preview' } },
3135    cpil => { #10
3136        Name => 'Compilation',
3137        Format => 'int8u', #27 (ref 23 contradicts what AtomicParsley actually writes, which is int8s)
3138        Writable => 'int8s',
3139        PrintConv => { 0 => 'No', 1 => 'Yes' },
3140    },
3141    disk => {
3142        Name => 'DiskNumber',
3143        Format => 'undef',  # (necessary to prevent decoding as string!)
3144        ValueConv => q{
3145            return \$val unless length($val) >= 6;
3146            my @a = unpack 'x2nn', $val;
3147            return $a[1] ? join(' of ', @a) : $a[0];
3148        },
3149        ValueConvInv => q{
3150            my @a = $val =~ /\d+/g;
3151            return undef if @a == 0 or @a > 2;
3152            push @a, 0 if @a == 1;
3153            return pack('n3', 0, @a);
3154        },
3155    },
3156    pgap => { #10
3157        Name => 'PlayGap',
3158        Format => 'int8u', #23
3159        Writable => 'int8s', #27
3160        PrintConv => {
3161            0 => 'Insert Gap',
3162            1 => 'No Gap',
3163        },
3164    },
3165    tmpo => {
3166        Name => 'BeatsPerMinute',
3167        # marked as boolean but really int16u in my sample
3168        # (but written as int16s by iTunes and AtomicParsley, ref forum11506)
3169        Format => 'int16u',
3170        Writable => 'int16s',
3171    },
3172    trkn => {
3173        Name => 'TrackNumber',
3174        Format => 'undef',  # (necessary to prevent decoding as string!)
3175        ValueConv => q{
3176            return \$val unless length($val) >= 6;
3177            my @a = unpack 'x2nn', $val;
3178            return $a[1] ? join(' of ', @a) : $a[0];
3179        },
3180        # (see forum11501 for discussion about the format used)
3181        ValueConvInv => q{
3182            my @a = $val =~ /\d+/g;
3183            return undef if @a == 0 or @a > 2;
3184            push @a, 0 if @a == 1;
3185            return pack('n4', 0, @a, 0);
3186        },
3187    },
3188#
3189# Note: it is possible that the tags below are not being decoded properly
3190# because I don't have samples to verify many of these - PH
3191#
3192    akID => { #10
3193        Name => 'AppleStoreAccountType',
3194        Format => 'int8u', #24
3195        Writable => 'int8s', #27
3196        PrintConv => {
3197            0 => 'iTunes',
3198            1 => 'AOL',
3199        },
3200    },
3201    albm => { Name => 'Album', Avoid => 1 }, #(ffmpeg source)
3202    apID => 'AppleStoreAccount',
3203    atID => { #10 (or TV series)
3204        Name => 'AlbumTitleID',
3205        Format => 'int32u',
3206        Writable => 'int32s', #27
3207    },
3208    auth => { Name => 'Author', Groups => { 2 => 'Author' } },
3209    catg => 'Category', #7
3210    cnID => { #10
3211        Name => 'AppleStoreCatalogID',
3212        Format => 'int32u',
3213        Writable => 'int32s', #27
3214    },
3215    cprt => { Name => 'Copyright', Groups => { 2 => 'Author' } },
3216    dscp => { Name => 'Description', Avoid => 1 },
3217    desc => { Name => 'Description', Avoid => 1 }, #7
3218    gnre => { #10
3219        Name => 'Genre',
3220        Avoid => 1,
3221        # (Note: see https://exiftool.org/forum/index.php?topic=11537.0)
3222        Format => 'undef',
3223        ValueConv => 'unpack("n",$val)',
3224        ValueConvInv => '$val =~ /^\d+$/ ? pack("n",$val) : undef',
3225        PrintConv => q{
3226            return $val unless $val =~ /^\d+$/;
3227            require Image::ExifTool::ID3;
3228            Image::ExifTool::ID3::PrintGenre($val - 1); # note the "- 1"
3229        },
3230        PrintConvInv => q{
3231            return $val if $val =~ /^[0-9]+$/;
3232            require Image::ExifTool::ID3;
3233            my $id = Image::ExifTool::ID3::GetGenreID($val);
3234            return unless defined $id and $id =~ /^\d+$/;
3235            return $id + 1;
3236        },
3237    },
3238    egid => 'EpisodeGlobalUniqueID', #7
3239    geID => { #10
3240        Name => 'GenreID',
3241        Format => 'int32u',
3242        Writable => 'int32s', #27
3243        SeparateTable => 1,
3244        # the following lookup is based on http://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/genres
3245        # (see scripts/parse_genre to parse genre JSON file from above)
3246        PrintConv => { #21/PH
3247            2 => 'Music|Blues',
3248            3 => 'Music|Comedy',
3249            4 => "Music|Children's Music",
3250            5 => 'Music|Classical',
3251            6 => 'Music|Country',
3252            7 => 'Music|Electronic',
3253            8 => 'Music|Holiday',
3254            9 => 'Music|Classical|Opera',
3255            10 => 'Music|Singer/Songwriter',
3256            11 => 'Music|Jazz',
3257            12 => 'Music|Latino',
3258            13 => 'Music|New Age',
3259            14 => 'Music|Pop',
3260            15 => 'Music|R&B/Soul',
3261            16 => 'Music|Soundtrack',
3262            17 => 'Music|Dance',
3263            18 => 'Music|Hip-Hop/Rap',
3264            19 => 'Music|World',
3265            20 => 'Music|Alternative',
3266            21 => 'Music|Rock',
3267            22 => 'Music|Christian & Gospel',
3268            23 => 'Music|Vocal',
3269            24 => 'Music|Reggae',
3270            25 => 'Music|Easy Listening',
3271            26 => 'Podcasts',
3272            27 => 'Music|J-Pop',
3273            28 => 'Music|Enka',
3274            29 => 'Music|Anime',
3275            30 => 'Music|Kayokyoku',
3276            31 => 'Music Videos',
3277            32 => 'TV Shows',
3278            33 => 'Movies',
3279            34 => 'Music',
3280            35 => 'iPod Games',
3281            36 => 'App Store',
3282            37 => 'Tones',
3283            38 => 'Books',
3284            39 => 'Mac App Store',
3285            40 => 'Textbooks',
3286            50 => 'Music|Fitness & Workout',
3287            51 => 'Music|Pop|K-Pop',
3288            52 => 'Music|Karaoke',
3289            53 => 'Music|Instrumental',
3290            74 => 'Audiobooks|News',
3291            75 => 'Audiobooks|Programs & Performances',
3292            500 => 'Fitness Music',
3293            501 => 'Fitness Music|Pop',
3294            502 => 'Fitness Music|Dance',
3295            503 => 'Fitness Music|Hip-Hop',
3296            504 => 'Fitness Music|Rock',
3297            505 => 'Fitness Music|Alt/Indie',
3298            506 => 'Fitness Music|Latino',
3299            507 => 'Fitness Music|Country',
3300            508 => 'Fitness Music|World',
3301            509 => 'Fitness Music|New Age',
3302            510 => 'Fitness Music|Classical',
3303            1001 => 'Music|Alternative|College Rock',
3304            1002 => 'Music|Alternative|Goth Rock',
3305            1003 => 'Music|Alternative|Grunge',
3306            1004 => 'Music|Alternative|Indie Rock',
3307            1005 => 'Music|Alternative|New Wave',
3308            1006 => 'Music|Alternative|Punk',
3309            1007 => 'Music|Blues|Chicago Blues',
3310            1009 => 'Music|Blues|Classic Blues',
3311            1010 => 'Music|Blues|Contemporary Blues',
3312            1011 => 'Music|Blues|Country Blues',
3313            1012 => 'Music|Blues|Delta Blues',
3314            1013 => 'Music|Blues|Electric Blues',
3315            1014 => "Music|Children's Music|Lullabies",
3316            1015 => "Music|Children's Music|Sing-Along",
3317            1016 => "Music|Children's Music|Stories",
3318            1017 => 'Music|Classical|Avant-Garde',
3319            1018 => 'Music|Classical|Baroque Era',
3320            1019 => 'Music|Classical|Chamber Music',
3321            1020 => 'Music|Classical|Chant',
3322            1021 => 'Music|Classical|Choral',
3323            1022 => 'Music|Classical|Classical Crossover',
3324            1023 => 'Music|Classical|Early Music',
3325            1024 => 'Music|Classical|Impressionist',
3326            1025 => 'Music|Classical|Medieval Era',
3327            1026 => 'Music|Classical|Minimalism',
3328            1027 => 'Music|Classical|Modern Era',
3329            1028 => 'Music|Classical|Opera',
3330            1029 => 'Music|Classical|Orchestral',
3331            1030 => 'Music|Classical|Renaissance',
3332            1031 => 'Music|Classical|Romantic Era',
3333            1032 => 'Music|Classical|Wedding Music',
3334            1033 => 'Music|Country|Alternative Country',
3335            1034 => 'Music|Country|Americana',
3336            1035 => 'Music|Country|Bluegrass',
3337            1036 => 'Music|Country|Contemporary Bluegrass',
3338            1037 => 'Music|Country|Contemporary Country',
3339            1038 => 'Music|Country|Country Gospel',
3340            1039 => 'Music|Country|Honky Tonk',
3341            1040 => 'Music|Country|Outlaw Country',
3342            1041 => 'Music|Country|Traditional Bluegrass',
3343            1042 => 'Music|Country|Traditional Country',
3344            1043 => 'Music|Country|Urban Cowboy',
3345            1044 => 'Music|Dance|Breakbeat',
3346            1045 => 'Music|Dance|Exercise',
3347            1046 => 'Music|Dance|Garage',
3348            1047 => 'Music|Dance|Hardcore',
3349            1048 => 'Music|Dance|House',
3350            1049 => "Music|Dance|Jungle/Drum'n'bass",
3351            1050 => 'Music|Dance|Techno',
3352            1051 => 'Music|Dance|Trance',
3353            1052 => 'Music|Jazz|Big Band',
3354            1053 => 'Music|Jazz|Bop',
3355            1054 => 'Music|Easy Listening|Lounge',
3356            1055 => 'Music|Easy Listening|Swing',
3357            1056 => 'Music|Electronic|Ambient',
3358            1057 => 'Music|Electronic|Downtempo',
3359            1058 => 'Music|Electronic|Electronica',
3360            1060 => 'Music|Electronic|IDM/Experimental',
3361            1061 => 'Music|Electronic|Industrial',
3362            1062 => 'Music|Singer/Songwriter|Alternative Folk',
3363            1063 => 'Music|Singer/Songwriter|Contemporary Folk',
3364            1064 => 'Music|Singer/Songwriter|Contemporary Singer/Songwriter',
3365            1065 => 'Music|Singer/Songwriter|Folk-Rock',
3366            1066 => 'Music|Singer/Songwriter|New Acoustic',
3367            1067 => 'Music|Singer/Songwriter|Traditional Folk',
3368            1068 => 'Music|Hip-Hop/Rap|Alternative Rap',
3369            1069 => 'Music|Hip-Hop/Rap|Dirty South',
3370            1070 => 'Music|Hip-Hop/Rap|East Coast Rap',
3371            1071 => 'Music|Hip-Hop/Rap|Gangsta Rap',
3372            1072 => 'Music|Hip-Hop/Rap|Hardcore Rap',
3373            1073 => 'Music|Hip-Hop/Rap|Hip-Hop',
3374            1074 => 'Music|Hip-Hop/Rap|Latin Rap',
3375            1075 => 'Music|Hip-Hop/Rap|Old School Rap',
3376            1076 => 'Music|Hip-Hop/Rap|Rap',
3377            1077 => 'Music|Hip-Hop/Rap|Underground Rap',
3378            1078 => 'Music|Hip-Hop/Rap|West Coast Rap',
3379            1079 => 'Music|Holiday|Chanukah',
3380            1080 => 'Music|Holiday|Christmas',
3381            1081 => "Music|Holiday|Christmas: Children's",
3382            1082 => 'Music|Holiday|Christmas: Classic',
3383            1083 => 'Music|Holiday|Christmas: Classical',
3384            1084 => 'Music|Holiday|Christmas: Jazz',
3385            1085 => 'Music|Holiday|Christmas: Modern',
3386            1086 => 'Music|Holiday|Christmas: Pop',
3387            1087 => 'Music|Holiday|Christmas: R&B',
3388            1088 => 'Music|Holiday|Christmas: Religious',
3389            1089 => 'Music|Holiday|Christmas: Rock',
3390            1090 => 'Music|Holiday|Easter',
3391            1091 => 'Music|Holiday|Halloween',
3392            1092 => 'Music|Holiday|Holiday: Other',
3393            1093 => 'Music|Holiday|Thanksgiving',
3394            1094 => 'Music|Christian & Gospel|CCM',
3395            1095 => 'Music|Christian & Gospel|Christian Metal',
3396            1096 => 'Music|Christian & Gospel|Christian Pop',
3397            1097 => 'Music|Christian & Gospel|Christian Rap',
3398            1098 => 'Music|Christian & Gospel|Christian Rock',
3399            1099 => 'Music|Christian & Gospel|Classic Christian',
3400            1100 => 'Music|Christian & Gospel|Contemporary Gospel',
3401            1101 => 'Music|Christian & Gospel|Gospel',
3402            1103 => 'Music|Christian & Gospel|Praise & Worship',
3403            1104 => 'Music|Christian & Gospel|Southern Gospel',
3404            1105 => 'Music|Christian & Gospel|Traditional Gospel',
3405            1106 => 'Music|Jazz|Avant-Garde Jazz',
3406            1107 => 'Music|Jazz|Contemporary Jazz',
3407            1108 => 'Music|Jazz|Crossover Jazz',
3408            1109 => 'Music|Jazz|Dixieland',
3409            1110 => 'Music|Jazz|Fusion',
3410            1111 => 'Music|Jazz|Latin Jazz',
3411            1112 => 'Music|Jazz|Mainstream Jazz',
3412            1113 => 'Music|Jazz|Ragtime',
3413            1114 => 'Music|Jazz|Smooth Jazz',
3414            1115 => 'Music|Latino|Latin Jazz',
3415            1116 => 'Music|Latino|Contemporary Latin',
3416            1117 => 'Music|Latino|Pop Latino',
3417            1118 => 'Music|Latino|Raices', # (Ra&iacute;ces)
3418            1119 => 'Music|Latino|Urbano latino',
3419            1120 => 'Music|Latino|Baladas y Boleros',
3420            1121 => 'Music|Latino|Rock y Alternativo',
3421            1122 => 'Music|Brazilian',
3422            1123 => 'Music|Latino|Musica Mexicana', # (M&uacute;sica Mexicana)
3423            1124 => 'Music|Latino|Musica tropical', # (M&uacute;sica tropical)
3424            1125 => 'Music|New Age|Environmental',
3425            1126 => 'Music|New Age|Healing',
3426            1127 => 'Music|New Age|Meditation',
3427            1128 => 'Music|New Age|Nature',
3428            1129 => 'Music|New Age|Relaxation',
3429            1130 => 'Music|New Age|Travel',
3430            1131 => 'Music|Pop|Adult Contemporary',
3431            1132 => 'Music|Pop|Britpop',
3432            1133 => 'Music|Pop|Pop/Rock',
3433            1134 => 'Music|Pop|Soft Rock',
3434            1135 => 'Music|Pop|Teen Pop',
3435            1136 => 'Music|R&B/Soul|Contemporary R&B',
3436            1137 => 'Music|R&B/Soul|Disco',
3437            1138 => 'Music|R&B/Soul|Doo Wop',
3438            1139 => 'Music|R&B/Soul|Funk',
3439            1140 => 'Music|R&B/Soul|Motown',
3440            1141 => 'Music|R&B/Soul|Neo-Soul',
3441            1142 => 'Music|R&B/Soul|Quiet Storm',
3442            1143 => 'Music|R&B/Soul|Soul',
3443            1144 => 'Music|Rock|Adult Alternative',
3444            1145 => 'Music|Rock|American Trad Rock',
3445            1146 => 'Music|Rock|Arena Rock',
3446            1147 => 'Music|Rock|Blues-Rock',
3447            1148 => 'Music|Rock|British Invasion',
3448            1149 => 'Music|Rock|Death Metal/Black Metal',
3449            1150 => 'Music|Rock|Glam Rock',
3450            1151 => 'Music|Rock|Hair Metal',
3451            1152 => 'Music|Rock|Hard Rock',
3452            1153 => 'Music|Rock|Metal',
3453            1154 => 'Music|Rock|Jam Bands',
3454            1155 => 'Music|Rock|Prog-Rock/Art Rock',
3455            1156 => 'Music|Rock|Psychedelic',
3456            1157 => 'Music|Rock|Rock & Roll',
3457            1158 => 'Music|Rock|Rockabilly',
3458            1159 => 'Music|Rock|Roots Rock',
3459            1160 => 'Music|Rock|Singer/Songwriter',
3460            1161 => 'Music|Rock|Southern Rock',
3461            1162 => 'Music|Rock|Surf',
3462            1163 => 'Music|Rock|Tex-Mex',
3463            1165 => 'Music|Soundtrack|Foreign Cinema',
3464            1166 => 'Music|Soundtrack|Musicals',
3465            1167 => 'Music|Comedy|Novelty',
3466            1168 => 'Music|Soundtrack|Original Score',
3467            1169 => 'Music|Soundtrack|Soundtrack',
3468            1171 => 'Music|Comedy|Standup Comedy',
3469            1172 => 'Music|Soundtrack|TV Soundtrack',
3470            1173 => 'Music|Vocal|Standards',
3471            1174 => 'Music|Vocal|Traditional Pop',
3472            1175 => 'Music|Jazz|Vocal Jazz',
3473            1176 => 'Music|Vocal|Vocal Pop',
3474            1177 => 'Music|African|Afro-Beat',
3475            1178 => 'Music|African|Afro-Pop',
3476            1179 => 'Music|World|Cajun',
3477            1180 => 'Music|World|Celtic',
3478            1181 => 'Music|World|Celtic Folk',
3479            1182 => 'Music|World|Contemporary Celtic',
3480            1183 => 'Music|Reggae|Modern Dancehall',
3481            1184 => 'Music|World|Drinking Songs',
3482            1185 => 'Music|Indian|Indian Pop',
3483            1186 => 'Music|World|Japanese Pop',
3484            1187 => 'Music|World|Klezmer',
3485            1188 => 'Music|World|Polka',
3486            1189 => 'Music|World|Traditional Celtic',
3487            1190 => 'Music|World|Worldbeat',
3488            1191 => 'Music|World|Zydeco',
3489            1192 => 'Music|Reggae|Roots Reggae',
3490            1193 => 'Music|Reggae|Dub',
3491            1194 => 'Music|Reggae|Ska',
3492            1195 => 'Music|World|Caribbean',
3493            1196 => 'Music|World|South America',
3494            1197 => 'Music|Arabic',
3495            1198 => 'Music|World|North America',
3496            1199 => 'Music|World|Hawaii',
3497            1200 => 'Music|World|Australia',
3498            1201 => 'Music|World|Japan',
3499            1202 => 'Music|World|France',
3500            1203 => 'Music|African',
3501            1204 => 'Music|World|Asia',
3502            1205 => 'Music|World|Europe',
3503            1206 => 'Music|World|South Africa',
3504            1207 => 'Music|Jazz|Hard Bop',
3505            1208 => 'Music|Jazz|Trad Jazz',
3506            1209 => 'Music|Jazz|Cool Jazz',
3507            1210 => 'Music|Blues|Acoustic Blues',
3508            1211 => 'Music|Classical|High Classical',
3509            1220 => 'Music|Brazilian|Axe', # (Ax&eacute;)
3510            1221 => 'Music|Brazilian|Bossa Nova',
3511            1222 => 'Music|Brazilian|Choro',
3512            1223 => 'Music|Brazilian|Forro', # (Forr&oacute;)
3513            1224 => 'Music|Brazilian|Frevo',
3514            1225 => 'Music|Brazilian|MPB',
3515            1226 => 'Music|Brazilian|Pagode',
3516            1227 => 'Music|Brazilian|Samba',
3517            1228 => 'Music|Brazilian|Sertanejo',
3518            1229 => 'Music|Brazilian|Baile Funk',
3519            1230 => 'Music|Alternative|Chinese Alt',
3520            1231 => 'Music|Alternative|Korean Indie',
3521            1232 => 'Music|Chinese',
3522            1233 => 'Music|Chinese|Chinese Classical',
3523            1234 => 'Music|Chinese|Chinese Flute',
3524            1235 => 'Music|Chinese|Chinese Opera',
3525            1236 => 'Music|Chinese|Chinese Orchestral',
3526            1237 => 'Music|Chinese|Chinese Regional Folk',
3527            1238 => 'Music|Chinese|Chinese Strings',
3528            1239 => 'Music|Chinese|Taiwanese Folk',
3529            1240 => 'Music|Chinese|Tibetan Native Music',
3530            1241 => 'Music|Hip-Hop/Rap|Chinese Hip-Hop',
3531            1242 => 'Music|Hip-Hop/Rap|Korean Hip-Hop',
3532            1243 => 'Music|Korean',
3533            1244 => 'Music|Korean|Korean Classical',
3534            1245 => 'Music|Korean|Korean Trad Song',
3535            1246 => 'Music|Korean|Korean Trad Instrumental',
3536            1247 => 'Music|Korean|Korean Trad Theater',
3537            1248 => 'Music|Rock|Chinese Rock',
3538            1249 => 'Music|Rock|Korean Rock',
3539            1250 => 'Music|Pop|C-Pop',
3540            1251 => 'Music|Pop|Cantopop/HK-Pop',
3541            1252 => 'Music|Pop|Korean Folk-Pop',
3542            1253 => 'Music|Pop|Mandopop',
3543            1254 => 'Music|Pop|Tai-Pop',
3544            1255 => 'Music|Pop|Malaysian Pop',
3545            1256 => 'Music|Pop|Pinoy Pop',
3546            1257 => 'Music|Pop|Original Pilipino Music',
3547            1258 => 'Music|Pop|Manilla Sound',
3548            1259 => 'Music|Pop|Indo Pop',
3549            1260 => 'Music|Pop|Thai Pop',
3550            1261 => 'Music|Vocal|Trot',
3551            1262 => 'Music|Indian',
3552            1263 => 'Music|Indian|Bollywood',
3553            1264 => 'Music|Indian|Regional Indian|Tamil',
3554            1265 => 'Music|Indian|Regional Indian|Telugu',
3555            1266 => 'Music|Indian|Regional Indian',
3556            1267 => 'Music|Indian|Devotional & Spiritual',
3557            1268 => 'Music|Indian|Sufi',
3558            1269 => 'Music|Indian|Indian Classical',
3559            1270 => 'Music|Russian|Russian Chanson',
3560            1271 => 'Music|World|Dini',
3561            1272 => 'Music|Turkish|Halk',
3562            1273 => 'Music|Turkish|Sanat',
3563            1274 => 'Music|World|Dangdut',
3564            1275 => 'Music|World|Indonesian Religious',
3565            1276 => 'Music|World|Calypso',
3566            1277 => 'Music|World|Soca',
3567            1278 => 'Music|Indian|Ghazals',
3568            1279 => 'Music|Indian|Indian Folk',
3569            1280 => 'Music|Turkish|Arabesque',
3570            1281 => 'Music|African|Afrikaans',
3571            1282 => 'Music|World|Farsi',
3572            1283 => 'Music|World|Israeli',
3573            1284 => 'Music|Arabic|Khaleeji',
3574            1285 => 'Music|Arabic|North African',
3575            1286 => 'Music|Arabic|Arabic Pop',
3576            1287 => 'Music|Arabic|Islamic',
3577            1288 => 'Music|Soundtrack|Sound Effects',
3578            1289 => 'Music|Folk',
3579            1290 => 'Music|Orchestral',
3580            1291 => 'Music|Marching',
3581            1293 => 'Music|Pop|Oldies',
3582            1294 => 'Music|Country|Thai Country',
3583            1295 => 'Music|World|Flamenco',
3584            1296 => 'Music|World|Tango',
3585            1297 => 'Music|World|Fado',
3586            1298 => 'Music|World|Iberia',
3587            1299 => 'Music|Russian',
3588            1300 => 'Music|Turkish',
3589            1301 => 'Podcasts|Arts',
3590            1302 => 'Podcasts|Society & Culture|Personal Journals',
3591            1303 => 'Podcasts|Comedy',
3592            1304 => 'Podcasts|Education',
3593            1305 => 'Podcasts|Kids & Family',
3594            1306 => 'Podcasts|Arts|Food',
3595            1307 => 'Podcasts|Health',
3596            1309 => 'Podcasts|TV & Film',
3597            1310 => 'Podcasts|Music',
3598            1311 => 'Podcasts|News & Politics',
3599            1314 => 'Podcasts|Religion & Spirituality',
3600            1315 => 'Podcasts|Science & Medicine',
3601            1316 => 'Podcasts|Sports & Recreation',
3602            1318 => 'Podcasts|Technology',
3603            1320 => 'Podcasts|Society & Culture|Places & Travel',
3604            1321 => 'Podcasts|Business',
3605            1323 => 'Podcasts|Games & Hobbies',
3606            1324 => 'Podcasts|Society & Culture',
3607            1325 => 'Podcasts|Government & Organizations',
3608            1337 => 'Music Videos|Classical|Piano',
3609            1401 => 'Podcasts|Arts|Literature',
3610            1402 => 'Podcasts|Arts|Design',
3611            1404 => 'Podcasts|Games & Hobbies|Video Games',
3612            1405 => 'Podcasts|Arts|Performing Arts',
3613            1406 => 'Podcasts|Arts|Visual Arts',
3614            1410 => 'Podcasts|Business|Careers',
3615            1412 => 'Podcasts|Business|Investing',
3616            1413 => 'Podcasts|Business|Management & Marketing',
3617            1415 => 'Podcasts|Education|K-12',
3618            1416 => 'Podcasts|Education|Higher Education',
3619            1417 => 'Podcasts|Health|Fitness & Nutrition',
3620            1420 => 'Podcasts|Health|Self-Help',
3621            1421 => 'Podcasts|Health|Sexuality',
3622            1438 => 'Podcasts|Religion & Spirituality|Buddhism',
3623            1439 => 'Podcasts|Religion & Spirituality|Christianity',
3624            1440 => 'Podcasts|Religion & Spirituality|Islam',
3625            1441 => 'Podcasts|Religion & Spirituality|Judaism',
3626            1443 => 'Podcasts|Society & Culture|Philosophy',
3627            1444 => 'Podcasts|Religion & Spirituality|Spirituality',
3628            1446 => 'Podcasts|Technology|Gadgets',
3629            1448 => 'Podcasts|Technology|Tech News',
3630            1450 => 'Podcasts|Technology|Podcasting',
3631            1454 => 'Podcasts|Games & Hobbies|Automotive',
3632            1455 => 'Podcasts|Games & Hobbies|Aviation',
3633            1456 => 'Podcasts|Sports & Recreation|Outdoor',
3634            1459 => 'Podcasts|Arts|Fashion & Beauty',
3635            1460 => 'Podcasts|Games & Hobbies|Hobbies',
3636            1461 => 'Podcasts|Games & Hobbies|Other Games',
3637            1462 => 'Podcasts|Society & Culture|History',
3638            1463 => 'Podcasts|Religion & Spirituality|Hinduism',
3639            1464 => 'Podcasts|Religion & Spirituality|Other',
3640            1465 => 'Podcasts|Sports & Recreation|Professional',
3641            1466 => 'Podcasts|Sports & Recreation|College & High School',
3642            1467 => 'Podcasts|Sports & Recreation|Amateur',
3643            1468 => 'Podcasts|Education|Educational Technology',
3644            1469 => 'Podcasts|Education|Language Courses',
3645            1470 => 'Podcasts|Education|Training',
3646            1471 => 'Podcasts|Business|Business News',
3647            1472 => 'Podcasts|Business|Shopping',
3648            1473 => 'Podcasts|Government & Organizations|National',
3649            1474 => 'Podcasts|Government & Organizations|Regional',
3650            1475 => 'Podcasts|Government & Organizations|Local',
3651            1476 => 'Podcasts|Government & Organizations|Non-Profit',
3652            1477 => 'Podcasts|Science & Medicine|Natural Sciences',
3653            1478 => 'Podcasts|Science & Medicine|Medicine',
3654            1479 => 'Podcasts|Science & Medicine|Social Sciences',
3655            1480 => 'Podcasts|Technology|Software How-To',
3656            1481 => 'Podcasts|Health|Alternative Health',
3657            1482 => 'Podcasts|Arts|Books',
3658            1483 => 'Podcasts|Fiction',
3659            1484 => 'Podcasts|Fiction|Drama',
3660            1485 => 'Podcasts|Fiction|Science Fiction',
3661            1486 => 'Podcasts|Fiction|Comedy Fiction',
3662            1487 => 'Podcasts|History',
3663            1488 => 'Podcasts|True Crime',
3664            1489 => 'Podcasts|News',
3665            1490 => 'Podcasts|News|Business News',
3666            1491 => 'Podcasts|Business|Management',
3667            1492 => 'Podcasts|Business|Marketing',
3668            1493 => 'Podcasts|Business|Entrepreneurship',
3669            1494 => 'Podcasts|Business|Non-Profit',
3670            1495 => 'Podcasts|Comedy|Improv',
3671            1496 => 'Podcasts|Comedy|Comedy Interviews',
3672            1497 => 'Podcasts|Comedy|Stand-Up',
3673            1498 => 'Podcasts|Education|Language Learning',
3674            1499 => 'Podcasts|Education|How To',
3675            1500 => 'Podcasts|Education|Self-Improvement',
3676            1501 => 'Podcasts|Education|Courses',
3677            1502 => 'Podcasts|Leisure',
3678            1503 => 'Podcasts|Leisure|Automotive',
3679            1504 => 'Podcasts|Leisure|Aviation',
3680            1505 => 'Podcasts|Leisure|Hobbies',
3681            1506 => 'Podcasts|Leisure|Crafts',
3682            1507 => 'Podcasts|Leisure|Games',
3683            1508 => 'Podcasts|Leisure|Home & Garden',
3684            1509 => 'Podcasts|Leisure|Video Games',
3685            1510 => 'Podcasts|Leisure|Animation & Manga',
3686            1511 => 'Podcasts|Government',
3687            1512 => 'Podcasts|Health & Fitness',
3688            1513 => 'Podcasts|Health & Fitness|Alternative Health',
3689            1514 => 'Podcasts|Health & Fitness|Fitness',
3690            1515 => 'Podcasts|Health & Fitness|Nutrition',
3691            1516 => 'Podcasts|Health & Fitness|Sexuality',
3692            1517 => 'Podcasts|Health & Fitness|Mental Health',
3693            1518 => 'Podcasts|Health & Fitness|Medicine',
3694            1519 => 'Podcasts|Kids & Family|Education for Kids',
3695            1520 => 'Podcasts|Kids & Family|Stories for Kids',
3696            1521 => 'Podcasts|Kids & Family|Parenting',
3697            1522 => 'Podcasts|Kids & Family|Pets & Animals',
3698            1523 => 'Podcasts|Music|Music Commentary',
3699            1524 => 'Podcasts|Music|Music History',
3700            1525 => 'Podcasts|Music|Music Interviews',
3701            1526 => 'Podcasts|News|Daily News',
3702            1527 => 'Podcasts|News|Politics',
3703            1528 => 'Podcasts|News|Tech News',
3704            1529 => 'Podcasts|News|Sports News',
3705            1530 => 'Podcasts|News|News Commentary',
3706            1531 => 'Podcasts|News|Entertainment News',
3707            1532 => 'Podcasts|Religion & Spirituality|Religion',
3708            1533 => 'Podcasts|Science',
3709            1534 => 'Podcasts|Science|Natural Sciences',
3710            1535 => 'Podcasts|Science|Social Sciences',
3711            1536 => 'Podcasts|Science|Mathematics',
3712            1537 => 'Podcasts|Science|Nature',
3713            1538 => 'Podcasts|Science|Astronomy',
3714            1539 => 'Podcasts|Science|Chemistry',
3715            1540 => 'Podcasts|Science|Earth Sciences',
3716            1541 => 'Podcasts|Science|Life Sciences',
3717            1542 => 'Podcasts|Science|Physics',
3718            1543 => 'Podcasts|Society & Culture|Documentary',
3719            1544 => 'Podcasts|Society & Culture|Relationships',
3720            1545 => 'Podcasts|Sports',
3721            1546 => 'Podcasts|Sports|Soccer',
3722            1547 => 'Podcasts|Sports|Football',
3723            1548 => 'Podcasts|Sports|Basketball',
3724            1549 => 'Podcasts|Sports|Baseball',
3725            1550 => 'Podcasts|Sports|Hockey',
3726            1551 => 'Podcasts|Sports|Running',
3727            1552 => 'Podcasts|Sports|Rugby',
3728            1553 => 'Podcasts|Sports|Golf',
3729            1554 => 'Podcasts|Sports|Cricket',
3730            1555 => 'Podcasts|Sports|Wrestling',
3731            1556 => 'Podcasts|Sports|Tennis',
3732            1557 => 'Podcasts|Sports|Volleyball',
3733            1558 => 'Podcasts|Sports|Swimming',
3734            1559 => 'Podcasts|Sports|Wilderness',
3735            1560 => 'Podcasts|Sports|Fantasy Sports',
3736            1561 => 'Podcasts|TV & Film|TV Reviews',
3737            1562 => 'Podcasts|TV & Film|After Shows',
3738            1563 => 'Podcasts|TV & Film|Film Reviews',
3739            1564 => 'Podcasts|TV & Film|Film History',
3740            1565 => 'Podcasts|TV & Film|Film Interviews',
3741            1602 => 'Music Videos|Blues',
3742            1603 => 'Music Videos|Comedy',
3743            1604 => "Music Videos|Children's Music",
3744            1605 => 'Music Videos|Classical',
3745            1606 => 'Music Videos|Country',
3746            1607 => 'Music Videos|Electronic',
3747            1608 => 'Music Videos|Holiday',
3748            1609 => 'Music Videos|Classical|Opera',
3749            1610 => 'Music Videos|Singer/Songwriter',
3750            1611 => 'Music Videos|Jazz',
3751            1612 => 'Music Videos|Latin',
3752            1613 => 'Music Videos|New Age',
3753            1614 => 'Music Videos|Pop',
3754            1615 => 'Music Videos|R&B/Soul',
3755            1616 => 'Music Videos|Soundtrack',
3756            1617 => 'Music Videos|Dance',
3757            1618 => 'Music Videos|Hip-Hop/Rap',
3758            1619 => 'Music Videos|World',
3759            1620 => 'Music Videos|Alternative',
3760            1621 => 'Music Videos|Rock',
3761            1622 => 'Music Videos|Christian & Gospel',
3762            1623 => 'Music Videos|Vocal',
3763            1624 => 'Music Videos|Reggae',
3764            1625 => 'Music Videos|Easy Listening',
3765            1626 => 'Music Videos|Podcasts',
3766            1627 => 'Music Videos|J-Pop',
3767            1628 => 'Music Videos|Enka',
3768            1629 => 'Music Videos|Anime',
3769            1630 => 'Music Videos|Kayokyoku',
3770            1631 => 'Music Videos|Disney',
3771            1632 => 'Music Videos|French Pop',
3772            1633 => 'Music Videos|German Pop',
3773            1634 => 'Music Videos|German Folk',
3774            1635 => 'Music Videos|Alternative|Chinese Alt',
3775            1636 => 'Music Videos|Alternative|Korean Indie',
3776            1637 => 'Music Videos|Chinese',
3777            1638 => 'Music Videos|Chinese|Chinese Classical',
3778            1639 => 'Music Videos|Chinese|Chinese Flute',
3779            1640 => 'Music Videos|Chinese|Chinese Opera',
3780            1641 => 'Music Videos|Chinese|Chinese Orchestral',
3781            1642 => 'Music Videos|Chinese|Chinese Regional Folk',
3782            1643 => 'Music Videos|Chinese|Chinese Strings',
3783            1644 => 'Music Videos|Chinese|Taiwanese Folk',
3784            1645 => 'Music Videos|Chinese|Tibetan Native Music',
3785            1646 => 'Music Videos|Hip-Hop/Rap|Chinese Hip-Hop',
3786            1647 => 'Music Videos|Hip-Hop/Rap|Korean Hip-Hop',
3787            1648 => 'Music Videos|Korean',
3788            1649 => 'Music Videos|Korean|Korean Classical',
3789            1650 => 'Music Videos|Korean|Korean Trad Song',
3790            1651 => 'Music Videos|Korean|Korean Trad Instrumental',
3791            1652 => 'Music Videos|Korean|Korean Trad Theater',
3792            1653 => 'Music Videos|Rock|Chinese Rock',
3793            1654 => 'Music Videos|Rock|Korean Rock',
3794            1655 => 'Music Videos|Pop|C-Pop',
3795            1656 => 'Music Videos|Pop|Cantopop/HK-Pop',
3796            1657 => 'Music Videos|Pop|Korean Folk-Pop',
3797            1658 => 'Music Videos|Pop|Mandopop',
3798            1659 => 'Music Videos|Pop|Tai-Pop',
3799            1660 => 'Music Videos|Pop|Malaysian Pop',
3800            1661 => 'Music Videos|Pop|Pinoy Pop',
3801            1662 => 'Music Videos|Pop|Original Pilipino Music',
3802            1663 => 'Music Videos|Pop|Manilla Sound',
3803            1664 => 'Music Videos|Pop|Indo Pop',
3804            1665 => 'Music Videos|Pop|Thai Pop',
3805            1666 => 'Music Videos|Vocal|Trot',
3806            1671 => 'Music Videos|Brazilian',
3807            1672 => 'Music Videos|Brazilian|Axe', # (Ax&eacute;)
3808            1673 => 'Music Videos|Brazilian|Baile Funk',
3809            1674 => 'Music Videos|Brazilian|Bossa Nova',
3810            1675 => 'Music Videos|Brazilian|Choro',
3811            1676 => 'Music Videos|Brazilian|Forro',
3812            1677 => 'Music Videos|Brazilian|Frevo',
3813            1678 => 'Music Videos|Brazilian|MPB',
3814            1679 => 'Music Videos|Brazilian|Pagode',
3815            1680 => 'Music Videos|Brazilian|Samba',
3816            1681 => 'Music Videos|Brazilian|Sertanejo',
3817            1682 => 'Music Videos|Classical|High Classical',
3818            1683 => 'Music Videos|Fitness & Workout',
3819            1684 => 'Music Videos|Instrumental',
3820            1685 => 'Music Videos|Jazz|Big Band',
3821            1686 => 'Music Videos|Pop|K-Pop',
3822            1687 => 'Music Videos|Karaoke',
3823            1688 => 'Music Videos|Rock|Heavy Metal',
3824            1689 => 'Music Videos|Spoken Word',
3825            1690 => 'Music Videos|Indian',
3826            1691 => 'Music Videos|Indian|Bollywood',
3827            1692 => 'Music Videos|Indian|Regional Indian|Tamil',
3828            1693 => 'Music Videos|Indian|Regional Indian|Telugu',
3829            1694 => 'Music Videos|Indian|Regional Indian',
3830            1695 => 'Music Videos|Indian|Devotional & Spiritual',
3831            1696 => 'Music Videos|Indian|Sufi',
3832            1697 => 'Music Videos|Indian|Indian Classical',
3833            1698 => 'Music Videos|Russian|Russian Chanson',
3834            1699 => 'Music Videos|World|Dini',
3835            1700 => 'Music Videos|Turkish|Halk',
3836            1701 => 'Music Videos|Turkish|Sanat',
3837            1702 => 'Music Videos|World|Dangdut',
3838            1703 => 'Music Videos|World|Indonesian Religious',
3839            1704 => 'Music Videos|Indian|Indian Pop',
3840            1705 => 'Music Videos|World|Calypso',
3841            1706 => 'Music Videos|World|Soca',
3842            1707 => 'Music Videos|Indian|Ghazals',
3843            1708 => 'Music Videos|Indian|Indian Folk',
3844            1709 => 'Music Videos|Turkish|Arabesque',
3845            1710 => 'Music Videos|African|Afrikaans',
3846            1711 => 'Music Videos|World|Farsi',
3847            1712 => 'Music Videos|World|Israeli',
3848            1713 => 'Music Videos|Arabic',
3849            1714 => 'Music Videos|Arabic|Khaleeji',
3850            1715 => 'Music Videos|Arabic|North African',
3851            1716 => 'Music Videos|Arabic|Arabic Pop',
3852            1717 => 'Music Videos|Arabic|Islamic',
3853            1718 => 'Music Videos|Soundtrack|Sound Effects',
3854            1719 => 'Music Videos|Folk',
3855            1720 => 'Music Videos|Orchestral',
3856            1721 => 'Music Videos|Marching',
3857            1723 => 'Music Videos|Pop|Oldies',
3858            1724 => 'Music Videos|Country|Thai Country',
3859            1725 => 'Music Videos|World|Flamenco',
3860            1726 => 'Music Videos|World|Tango',
3861            1727 => 'Music Videos|World|Fado',
3862            1728 => 'Music Videos|World|Iberia',
3863            1729 => 'Music Videos|Russian',
3864            1730 => 'Music Videos|Turkish',
3865            1731 => 'Music Videos|Alternative|College Rock',
3866            1732 => 'Music Videos|Alternative|Goth Rock',
3867            1733 => 'Music Videos|Alternative|Grunge',
3868            1734 => 'Music Videos|Alternative|Indie Rock',
3869            1735 => 'Music Videos|Alternative|New Wave',
3870            1736 => 'Music Videos|Alternative|Punk',
3871            1737 => 'Music Videos|Blues|Acoustic Blues',
3872            1738 => 'Music Videos|Blues|Chicago Blues',
3873            1739 => 'Music Videos|Blues|Classic Blues',
3874            1740 => 'Music Videos|Blues|Contemporary Blues',
3875            1741 => 'Music Videos|Blues|Country Blues',
3876            1742 => 'Music Videos|Blues|Delta Blues',
3877            1743 => 'Music Videos|Blues|Electric Blues',
3878            1744 => "Music Videos|Children's Music|Lullabies",
3879            1745 => "Music Videos|Children's Music|Sing-Along",
3880            1746 => "Music Videos|Children's Music|Stories",
3881            1747 => 'Music Videos|Christian & Gospel|CCM',
3882            1748 => 'Music Videos|Christian & Gospel|Christian Metal',
3883            1749 => 'Music Videos|Christian & Gospel|Christian Pop',
3884            1750 => 'Music Videos|Christian & Gospel|Christian Rap',
3885            1751 => 'Music Videos|Christian & Gospel|Christian Rock',
3886            1752 => 'Music Videos|Christian & Gospel|Classic Christian',
3887            1753 => 'Music Videos|Christian & Gospel|Contemporary Gospel',
3888            1754 => 'Music Videos|Christian & Gospel|Gospel',
3889            1755 => 'Music Videos|Christian & Gospel|Praise & Worship',
3890            1756 => 'Music Videos|Christian & Gospel|Southern Gospel',
3891            1757 => 'Music Videos|Christian & Gospel|Traditional Gospel',
3892            1758 => 'Music Videos|Classical|Avant-Garde',
3893            1759 => 'Music Videos|Classical|Baroque Era',
3894            1760 => 'Music Videos|Classical|Chamber Music',
3895            1761 => 'Music Videos|Classical|Chant',
3896            1762 => 'Music Videos|Classical|Choral',
3897            1763 => 'Music Videos|Classical|Classical Crossover',
3898            1764 => 'Music Videos|Classical|Early Music',
3899            1765 => 'Music Videos|Classical|Impressionist',
3900            1766 => 'Music Videos|Classical|Medieval Era',
3901            1767 => 'Music Videos|Classical|Minimalism',
3902            1768 => 'Music Videos|Classical|Modern Era',
3903            1769 => 'Music Videos|Classical|Orchestral',
3904            1770 => 'Music Videos|Classical|Renaissance',
3905            1771 => 'Music Videos|Classical|Romantic Era',
3906            1772 => 'Music Videos|Classical|Wedding Music',
3907            1773 => 'Music Videos|Comedy|Novelty',
3908            1774 => 'Music Videos|Comedy|Standup Comedy',
3909            1775 => 'Music Videos|Country|Alternative Country',
3910            1776 => 'Music Videos|Country|Americana',
3911            1777 => 'Music Videos|Country|Bluegrass',
3912            1778 => 'Music Videos|Country|Contemporary Bluegrass',
3913            1779 => 'Music Videos|Country|Contemporary Country',
3914            1780 => 'Music Videos|Country|Country Gospel',
3915            1781 => 'Music Videos|Country|Honky Tonk',
3916            1782 => 'Music Videos|Country|Outlaw Country',
3917            1783 => 'Music Videos|Country|Traditional Bluegrass',
3918            1784 => 'Music Videos|Country|Traditional Country',
3919            1785 => 'Music Videos|Country|Urban Cowboy',
3920            1786 => 'Music Videos|Dance|Breakbeat',
3921            1787 => 'Music Videos|Dance|Exercise',
3922            1788 => 'Music Videos|Dance|Garage',
3923            1789 => 'Music Videos|Dance|Hardcore',
3924            1790 => 'Music Videos|Dance|House',
3925            1791 => "Music Videos|Dance|Jungle/Drum'n'bass",
3926            1792 => 'Music Videos|Dance|Techno',
3927            1793 => 'Music Videos|Dance|Trance',
3928            1794 => 'Music Videos|Easy Listening|Lounge',
3929            1795 => 'Music Videos|Easy Listening|Swing',
3930            1796 => 'Music Videos|Electronic|Ambient',
3931            1797 => 'Music Videos|Electronic|Downtempo',
3932            1798 => 'Music Videos|Electronic|Electronica',
3933            1799 => 'Music Videos|Electronic|IDM/Experimental',
3934            1800 => 'Music Videos|Electronic|Industrial',
3935            1801 => 'Music Videos|Hip-Hop/Rap|Alternative Rap',
3936            1802 => 'Music Videos|Hip-Hop/Rap|Dirty South',
3937            1803 => 'Music Videos|Hip-Hop/Rap|East Coast Rap',
3938            1804 => 'Music Videos|Hip-Hop/Rap|Gangsta Rap',
3939            1805 => 'Music Videos|Hip-Hop/Rap|Hardcore Rap',
3940            1806 => 'Music Videos|Hip-Hop/Rap|Hip-Hop',
3941            1807 => 'Music Videos|Hip-Hop/Rap|Latin Rap',
3942            1808 => 'Music Videos|Hip-Hop/Rap|Old School Rap',
3943            1809 => 'Music Videos|Hip-Hop/Rap|Rap',
3944            1810 => 'Music Videos|Hip-Hop/Rap|Underground Rap',
3945            1811 => 'Music Videos|Hip-Hop/Rap|West Coast Rap',
3946            1812 => 'Music Videos|Holiday|Chanukah',
3947            1813 => 'Music Videos|Holiday|Christmas',
3948            1814 => "Music Videos|Holiday|Christmas: Children's",
3949            1815 => 'Music Videos|Holiday|Christmas: Classic',
3950            1816 => 'Music Videos|Holiday|Christmas: Classical',
3951            1817 => 'Music Videos|Holiday|Christmas: Jazz',
3952            1818 => 'Music Videos|Holiday|Christmas: Modern',
3953            1819 => 'Music Videos|Holiday|Christmas: Pop',
3954            1820 => 'Music Videos|Holiday|Christmas: R&B',
3955            1821 => 'Music Videos|Holiday|Christmas: Religious',
3956            1822 => 'Music Videos|Holiday|Christmas: Rock',
3957            1823 => 'Music Videos|Holiday|Easter',
3958            1824 => 'Music Videos|Holiday|Halloween',
3959            1825 => 'Music Videos|Holiday|Thanksgiving',
3960            1826 => 'Music Videos|Jazz|Avant-Garde Jazz',
3961            1828 => 'Music Videos|Jazz|Bop',
3962            1829 => 'Music Videos|Jazz|Contemporary Jazz',
3963            1830 => 'Music Videos|Jazz|Cool Jazz',
3964            1831 => 'Music Videos|Jazz|Crossover Jazz',
3965            1832 => 'Music Videos|Jazz|Dixieland',
3966            1833 => 'Music Videos|Jazz|Fusion',
3967            1834 => 'Music Videos|Jazz|Hard Bop',
3968            1835 => 'Music Videos|Jazz|Latin Jazz',
3969            1836 => 'Music Videos|Jazz|Mainstream Jazz',
3970            1837 => 'Music Videos|Jazz|Ragtime',
3971            1838 => 'Music Videos|Jazz|Smooth Jazz',
3972            1839 => 'Music Videos|Jazz|Trad Jazz',
3973            1840 => 'Music Videos|Latin|Alternative & Rock in Spanish',
3974            1841 => 'Music Videos|Latin|Baladas y Boleros',
3975            1842 => 'Music Videos|Latin|Contemporary Latin',
3976            1843 => 'Music Videos|Latin|Latin Jazz',
3977            1844 => 'Music Videos|Latin|Latin Urban',
3978            1845 => 'Music Videos|Latin|Pop in Spanish',
3979            1846 => 'Music Videos|Latin|Raices',
3980            1847 => 'Music Videos|Latin|Musica Mexicana', # (M&uacute;sica Mexicana)
3981            1848 => 'Music Videos|Latin|Salsa y Tropical',
3982            1849 => 'Music Videos|New Age|Healing',
3983            1850 => 'Music Videos|New Age|Meditation',
3984            1851 => 'Music Videos|New Age|Nature',
3985            1852 => 'Music Videos|New Age|Relaxation',
3986            1853 => 'Music Videos|New Age|Travel',
3987            1854 => 'Music Videos|Pop|Adult Contemporary',
3988            1855 => 'Music Videos|Pop|Britpop',
3989            1856 => 'Music Videos|Pop|Pop/Rock',
3990            1857 => 'Music Videos|Pop|Soft Rock',
3991            1858 => 'Music Videos|Pop|Teen Pop',
3992            1859 => 'Music Videos|R&B/Soul|Contemporary R&B',
3993            1860 => 'Music Videos|R&B/Soul|Disco',
3994            1861 => 'Music Videos|R&B/Soul|Doo Wop',
3995            1862 => 'Music Videos|R&B/Soul|Funk',
3996            1863 => 'Music Videos|R&B/Soul|Motown',
3997            1864 => 'Music Videos|R&B/Soul|Neo-Soul',
3998            1865 => 'Music Videos|R&B/Soul|Soul',
3999            1866 => 'Music Videos|Reggae|Modern Dancehall',
4000            1867 => 'Music Videos|Reggae|Dub',
4001            1868 => 'Music Videos|Reggae|Roots Reggae',
4002            1869 => 'Music Videos|Reggae|Ska',
4003            1870 => 'Music Videos|Rock|Adult Alternative',
4004            1871 => 'Music Videos|Rock|American Trad Rock',
4005            1872 => 'Music Videos|Rock|Arena Rock',
4006            1873 => 'Music Videos|Rock|Blues-Rock',
4007            1874 => 'Music Videos|Rock|British Invasion',
4008            1875 => 'Music Videos|Rock|Death Metal/Black Metal',
4009            1876 => 'Music Videos|Rock|Glam Rock',
4010            1877 => 'Music Videos|Rock|Hair Metal',
4011            1878 => 'Music Videos|Rock|Hard Rock',
4012            1879 => 'Music Videos|Rock|Jam Bands',
4013            1880 => 'Music Videos|Rock|Prog-Rock/Art Rock',
4014            1881 => 'Music Videos|Rock|Psychedelic',
4015            1882 => 'Music Videos|Rock|Rock & Roll',
4016            1883 => 'Music Videos|Rock|Rockabilly',
4017            1884 => 'Music Videos|Rock|Roots Rock',
4018            1885 => 'Music Videos|Rock|Singer/Songwriter',
4019            1886 => 'Music Videos|Rock|Southern Rock',
4020            1887 => 'Music Videos|Rock|Surf',
4021            1888 => 'Music Videos|Rock|Tex-Mex',
4022            1889 => 'Music Videos|Singer/Songwriter|Alternative Folk',
4023            1890 => 'Music Videos|Singer/Songwriter|Contemporary Folk',
4024            1891 => 'Music Videos|Singer/Songwriter|Contemporary Singer/Songwriter',
4025            1892 => 'Music Videos|Singer/Songwriter|Folk-Rock',
4026            1893 => 'Music Videos|Singer/Songwriter|New Acoustic',
4027            1894 => 'Music Videos|Singer/Songwriter|Traditional Folk',
4028            1895 => 'Music Videos|Soundtrack|Foreign Cinema',
4029            1896 => 'Music Videos|Soundtrack|Musicals',
4030            1897 => 'Music Videos|Soundtrack|Original Score',
4031            1898 => 'Music Videos|Soundtrack|Soundtrack',
4032            1899 => 'Music Videos|Soundtrack|TV Soundtrack',
4033            1900 => 'Music Videos|Vocal|Standards',
4034            1901 => 'Music Videos|Vocal|Traditional Pop',
4035            1902 => 'Music Videos|Jazz|Vocal Jazz',
4036            1903 => 'Music Videos|Vocal|Vocal Pop',
4037            1904 => 'Music Videos|African',
4038            1905 => 'Music Videos|African|Afro-Beat',
4039            1906 => 'Music Videos|African|Afro-Pop',
4040            1907 => 'Music Videos|World|Asia',
4041            1908 => 'Music Videos|World|Australia',
4042            1909 => 'Music Videos|World|Cajun',
4043            1910 => 'Music Videos|World|Caribbean',
4044            1911 => 'Music Videos|World|Celtic',
4045            1912 => 'Music Videos|World|Celtic Folk',
4046            1913 => 'Music Videos|World|Contemporary Celtic',
4047            1914 => 'Music Videos|World|Europe',
4048            1915 => 'Music Videos|World|France',
4049            1916 => 'Music Videos|World|Hawaii',
4050            1917 => 'Music Videos|World|Japan',
4051            1918 => 'Music Videos|World|Klezmer',
4052            1919 => 'Music Videos|World|North America',
4053            1920 => 'Music Videos|World|Polka',
4054            1921 => 'Music Videos|World|South Africa',
4055            1922 => 'Music Videos|World|South America',
4056            1923 => 'Music Videos|World|Traditional Celtic',
4057            1924 => 'Music Videos|World|Worldbeat',
4058            1925 => 'Music Videos|World|Zydeco',
4059            1926 => 'Music Videos|Christian & Gospel',
4060            1928 => 'Music Videos|Classical|Art Song',
4061            1929 => 'Music Videos|Classical|Brass & Woodwinds',
4062            1930 => 'Music Videos|Classical|Solo Instrumental',
4063            1931 => 'Music Videos|Classical|Contemporary Era',
4064            1932 => 'Music Videos|Classical|Oratorio',
4065            1933 => 'Music Videos|Classical|Cantata',
4066            1934 => 'Music Videos|Classical|Electronic',
4067            1935 => 'Music Videos|Classical|Sacred',
4068            1936 => 'Music Videos|Classical|Guitar',
4069            1938 => 'Music Videos|Classical|Violin',
4070            1939 => 'Music Videos|Classical|Cello',
4071            1940 => 'Music Videos|Classical|Percussion',
4072            1941 => 'Music Videos|Electronic|Dubstep',
4073            1942 => 'Music Videos|Electronic|Bass',
4074            1943 => 'Music Videos|Hip-Hop/Rap|UK Hip-Hop',
4075            1944 => 'Music Videos|Reggae|Lovers Rock',
4076            1945 => 'Music Videos|Alternative|EMO',
4077            1946 => 'Music Videos|Alternative|Pop Punk',
4078            1947 => 'Music Videos|Alternative|Indie Pop',
4079            1948 => 'Music Videos|New Age|Yoga',
4080            1949 => 'Music Videos|Pop|Tribute',
4081            1950 => 'Music Videos|Pop|Shows',
4082            1951 => 'Music Videos|Cuban',
4083            1952 => 'Music Videos|Cuban|Mambo',
4084            1953 => 'Music Videos|Cuban|Chachacha',
4085            1954 => 'Music Videos|Cuban|Guajira',
4086            1955 => 'Music Videos|Cuban|Son',
4087            1956 => 'Music Videos|Cuban|Bolero',
4088            1957 => 'Music Videos|Cuban|Guaracha',
4089            1958 => 'Music Videos|Cuban|Timba',
4090            1959 => 'Music Videos|Soundtrack|Video Game',
4091            1960 => 'Music Videos|Indian|Regional Indian|Punjabi|Punjabi Pop',
4092            1961 => 'Music Videos|Indian|Regional Indian|Bengali|Rabindra Sangeet',
4093            1962 => 'Music Videos|Indian|Regional Indian|Malayalam',
4094            1963 => 'Music Videos|Indian|Regional Indian|Kannada',
4095            1964 => 'Music Videos|Indian|Regional Indian|Marathi',
4096            1965 => 'Music Videos|Indian|Regional Indian|Gujarati',
4097            1966 => 'Music Videos|Indian|Regional Indian|Assamese',
4098            1967 => 'Music Videos|Indian|Regional Indian|Bhojpuri',
4099            1968 => 'Music Videos|Indian|Regional Indian|Haryanvi',
4100            1969 => 'Music Videos|Indian|Regional Indian|Odia',
4101            1970 => 'Music Videos|Indian|Regional Indian|Rajasthani',
4102            1971 => 'Music Videos|Indian|Regional Indian|Urdu',
4103            1972 => 'Music Videos|Indian|Regional Indian|Punjabi',
4104            1973 => 'Music Videos|Indian|Regional Indian|Bengali',
4105            1974 => 'Music Videos|Indian|Indian Classical|Carnatic Classical',
4106            1975 => 'Music Videos|Indian|Indian Classical|Hindustani Classical',
4107            1976 => 'Music Videos|African|Afro House',
4108            1977 => 'Music Videos|African|Afro Soul',
4109            1978 => 'Music Videos|African|Afrobeats',
4110            1979 => 'Music Videos|African|Benga',
4111            1980 => 'Music Videos|African|Bongo-Flava',
4112            1981 => 'Music Videos|African|Coupe-Decale',
4113            1982 => 'Music Videos|African|Gqom',
4114            1983 => 'Music Videos|African|Highlife',
4115            1984 => 'Music Videos|African|Kuduro',
4116            1985 => 'Music Videos|African|Kizomba',
4117            1986 => 'Music Videos|African|Kwaito',
4118            1987 => 'Music Videos|African|Mbalax',
4119            1988 => 'Music Videos|African|Ndombolo',
4120            1989 => 'Music Videos|African|Shangaan Electro',
4121            1990 => 'Music Videos|African|Soukous',
4122            1991 => 'Music Videos|African|Taarab',
4123            1992 => 'Music Videos|African|Zouglou',
4124            1993 => 'Music Videos|Turkish|Ozgun',
4125            1994 => 'Music Videos|Turkish|Fantezi',
4126            1995 => 'Music Videos|Turkish|Religious',
4127            1996 => 'Music Videos|Pop|Turkish Pop',
4128            1997 => 'Music Videos|Rock|Turkish Rock',
4129            1998 => 'Music Videos|Alternative|Turkish Alternative',
4130            1999 => 'Music Videos|Hip-Hop/Rap|Turkish Hip-Hop/Rap',
4131            2000 => 'Music Videos|African|Maskandi',
4132            2001 => 'Music Videos|Russian|Russian Romance',
4133            2002 => 'Music Videos|Russian|Russian Bard',
4134            2003 => 'Music Videos|Russian|Russian Pop',
4135            2004 => 'Music Videos|Russian|Russian Rock',
4136            2005 => 'Music Videos|Russian|Russian Hip-Hop',
4137            2006 => 'Music Videos|Arabic|Levant',
4138            2007 => 'Music Videos|Arabic|Levant|Dabke',
4139            2008 => 'Music Videos|Arabic|Maghreb Rai',
4140            2009 => 'Music Videos|Arabic|Khaleeji|Khaleeji Jalsat',
4141            2010 => 'Music Videos|Arabic|Khaleeji|Khaleeji Shailat',
4142            2011 => 'Music Videos|Tarab',
4143            2012 => 'Music Videos|Tarab|Iraqi Tarab',
4144            2013 => 'Music Videos|Tarab|Egyptian Tarab',
4145            2014 => 'Music Videos|Tarab|Khaleeji Tarab',
4146            2015 => 'Music Videos|Pop|Levant Pop',
4147            2016 => 'Music Videos|Pop|Iraqi Pop',
4148            2017 => 'Music Videos|Pop|Egyptian Pop',
4149            2018 => 'Music Videos|Pop|Maghreb Pop',
4150            2019 => 'Music Videos|Pop|Khaleeji Pop',
4151            2020 => 'Music Videos|Hip-Hop/Rap|Levant Hip-Hop',
4152            2021 => 'Music Videos|Hip-Hop/Rap|Egyptian Hip-Hop',
4153            2022 => 'Music Videos|Hip-Hop/Rap|Maghreb Hip-Hop',
4154            2023 => 'Music Videos|Hip-Hop/Rap|Khaleeji Hip-Hop',
4155            2024 => 'Music Videos|Alternative|Indie Levant',
4156            2025 => 'Music Videos|Alternative|Indie Egyptian',
4157            2026 => 'Music Videos|Alternative|Indie Maghreb',
4158            2027 => 'Music Videos|Electronic|Levant Electronic',
4159            2028 => "Music Videos|Electronic|Electro-Cha'abi",
4160            2029 => 'Music Videos|Electronic|Maghreb Electronic',
4161            2030 => 'Music Videos|Folk|Iraqi Folk',
4162            2031 => 'Music Videos|Folk|Khaleeji Folk',
4163            2032 => 'Music Videos|Dance|Maghreb Dance',
4164            4000 => 'TV Shows|Comedy',
4165            4001 => 'TV Shows|Drama',
4166            4002 => 'TV Shows|Animation',
4167            4003 => 'TV Shows|Action & Adventure',
4168            4004 => 'TV Shows|Classics',
4169            4005 => 'TV Shows|Kids & Family',
4170            4006 => 'TV Shows|Nonfiction',
4171            4007 => 'TV Shows|Reality TV',
4172            4008 => 'TV Shows|Sci-Fi & Fantasy',
4173            4009 => 'TV Shows|Sports',
4174            4010 => 'TV Shows|Teens',
4175            4011 => 'TV Shows|Latino TV',
4176            4401 => 'Movies|Action & Adventure',
4177            4402 => 'Movies|Anime',
4178            4403 => 'Movies|Classics',
4179            4404 => 'Movies|Comedy',
4180            4405 => 'Movies|Documentary',
4181            4406 => 'Movies|Drama',
4182            4407 => 'Movies|Foreign',
4183            4408 => 'Movies|Horror',
4184            4409 => 'Movies|Independent',
4185            4410 => 'Movies|Kids & Family',
4186            4411 => 'Movies|Musicals',
4187            4412 => 'Movies|Romance',
4188            4413 => 'Movies|Sci-Fi & Fantasy',
4189            4414 => 'Movies|Short Films',
4190            4415 => 'Movies|Special Interest',
4191            4416 => 'Movies|Thriller',
4192            4417 => 'Movies|Sports',
4193            4418 => 'Movies|Western',
4194            4419 => 'Movies|Urban',
4195            4420 => 'Movies|Holiday',
4196            4421 => 'Movies|Made for TV',
4197            4422 => 'Movies|Concert Films',
4198            4423 => 'Movies|Music Documentaries',
4199            4424 => 'Movies|Music Feature Films',
4200            4425 => 'Movies|Japanese Cinema',
4201            4426 => 'Movies|Jidaigeki',
4202            4427 => 'Movies|Tokusatsu',
4203            4428 => 'Movies|Korean Cinema',
4204            4429 => 'Movies|Russian',
4205            4430 => 'Movies|Turkish',
4206            4431 => 'Movies|Bollywood',
4207            4432 => 'Movies|Regional Indian',
4208            4433 => 'Movies|Middle Eastern',
4209            4434 => 'Movies|African',
4210            6000 => 'App Store|Business',
4211            6001 => 'App Store|Weather',
4212            6002 => 'App Store|Utilities',
4213            6003 => 'App Store|Travel',
4214            6004 => 'App Store|Sports',
4215            6005 => 'App Store|Social Networking',
4216            6006 => 'App Store|Reference',
4217            6007 => 'App Store|Productivity',
4218            6008 => 'App Store|Photo & Video',
4219            6009 => 'App Store|News',
4220            6010 => 'App Store|Navigation',
4221            6011 => 'App Store|Music',
4222            6012 => 'App Store|Lifestyle',
4223            6013 => 'App Store|Health & Fitness',
4224            6014 => 'App Store|Games',
4225            6015 => 'App Store|Finance',
4226            6016 => 'App Store|Entertainment',
4227            6017 => 'App Store|Education',
4228            6018 => 'App Store|Books',
4229            6020 => 'App Store|Medical',
4230            6021 => 'App Store|Magazines & Newspapers',
4231            6022 => 'App Store|Catalogs',
4232            6023 => 'App Store|Food & Drink',
4233            6024 => 'App Store|Shopping',
4234            6025 => 'App Store|Stickers',
4235            6026 => 'App Store|Developer Tools',
4236            6027 => 'App Store|Graphics & Design',
4237            7001 => 'App Store|Games|Action',
4238            7002 => 'App Store|Games|Adventure',
4239            7003 => 'App Store|Games|Casual',
4240            7004 => 'App Store|Games|Board',
4241            7005 => 'App Store|Games|Card',
4242            7006 => 'App Store|Games|Casino',
4243            7007 => 'App Store|Games|Dice',
4244            7008 => 'App Store|Games|Educational',
4245            7009 => 'App Store|Games|Family',
4246            7011 => 'App Store|Games|Music',
4247            7012 => 'App Store|Games|Puzzle',
4248            7013 => 'App Store|Games|Racing',
4249            7014 => 'App Store|Games|Role Playing',
4250            7015 => 'App Store|Games|Simulation',
4251            7016 => 'App Store|Games|Sports',
4252            7017 => 'App Store|Games|Strategy',
4253            7018 => 'App Store|Games|Trivia',
4254            7019 => 'App Store|Games|Word',
4255            8001 => 'Tones|Ringtones|Alternative',
4256            8002 => 'Tones|Ringtones|Blues',
4257            8003 => "Tones|Ringtones|Children's Music",
4258            8004 => 'Tones|Ringtones|Classical',
4259            8005 => 'Tones|Ringtones|Comedy',
4260            8006 => 'Tones|Ringtones|Country',
4261            8007 => 'Tones|Ringtones|Dance',
4262            8008 => 'Tones|Ringtones|Electronic',
4263            8009 => 'Tones|Ringtones|Enka',
4264            8010 => 'Tones|Ringtones|French Pop',
4265            8011 => 'Tones|Ringtones|German Folk',
4266            8012 => 'Tones|Ringtones|German Pop',
4267            8013 => 'Tones|Ringtones|Hip-Hop/Rap',
4268            8014 => 'Tones|Ringtones|Holiday',
4269            8015 => 'Tones|Ringtones|Inspirational',
4270            8016 => 'Tones|Ringtones|J-Pop',
4271            8017 => 'Tones|Ringtones|Jazz',
4272            8018 => 'Tones|Ringtones|Kayokyoku',
4273            8019 => 'Tones|Ringtones|Latin',
4274            8020 => 'Tones|Ringtones|New Age',
4275            8021 => 'Tones|Ringtones|Classical|Opera',
4276            8022 => 'Tones|Ringtones|Pop',
4277            8023 => 'Tones|Ringtones|R&B/Soul',
4278            8024 => 'Tones|Ringtones|Reggae',
4279            8025 => 'Tones|Ringtones|Rock',
4280            8026 => 'Tones|Ringtones|Singer/Songwriter',
4281            8027 => 'Tones|Ringtones|Soundtrack',
4282            8028 => 'Tones|Ringtones|Spoken Word',
4283            8029 => 'Tones|Ringtones|Vocal',
4284            8030 => 'Tones|Ringtones|World',
4285            8050 => 'Tones|Alert Tones|Sound Effects',
4286            8051 => 'Tones|Alert Tones|Dialogue',
4287            8052 => 'Tones|Alert Tones|Music',
4288            8053 => 'Tones|Ringtones',
4289            8054 => 'Tones|Alert Tones',
4290            8055 => 'Tones|Ringtones|Alternative|Chinese Alt',
4291            8056 => 'Tones|Ringtones|Alternative|College Rock',
4292            8057 => 'Tones|Ringtones|Alternative|Goth Rock',
4293            8058 => 'Tones|Ringtones|Alternative|Grunge',
4294            8059 => 'Tones|Ringtones|Alternative|Indie Rock',
4295            8060 => 'Tones|Ringtones|Alternative|Korean Indie',
4296            8061 => 'Tones|Ringtones|Alternative|New Wave',
4297            8062 => 'Tones|Ringtones|Alternative|Punk',
4298            8063 => 'Tones|Ringtones|Anime',
4299            8064 => 'Tones|Ringtones|Arabic',
4300            8065 => 'Tones|Ringtones|Arabic|Arabic Pop',
4301            8066 => 'Tones|Ringtones|Arabic|Islamic',
4302            8067 => 'Tones|Ringtones|Arabic|Khaleeji',
4303            8068 => 'Tones|Ringtones|Arabic|North African',
4304            8069 => 'Tones|Ringtones|Blues|Acoustic Blues',
4305            8070 => 'Tones|Ringtones|Blues|Chicago Blues',
4306            8071 => 'Tones|Ringtones|Blues|Classic Blues',
4307            8072 => 'Tones|Ringtones|Blues|Contemporary Blues',
4308            8073 => 'Tones|Ringtones|Blues|Country Blues',
4309            8074 => 'Tones|Ringtones|Blues|Delta Blues',
4310            8075 => 'Tones|Ringtones|Blues|Electric Blues',
4311            8076 => 'Tones|Ringtones|Brazilian',
4312            8077 => 'Tones|Ringtones|Brazilian|Axe', # (Ax&eacute;)
4313            8078 => 'Tones|Ringtones|Brazilian|Baile Funk',
4314            8079 => 'Tones|Ringtones|Brazilian|Bossa Nova',
4315            8080 => 'Tones|Ringtones|Brazilian|Choro',
4316            8081 => 'Tones|Ringtones|Brazilian|Forro', # (Forr&oacute;)
4317            8082 => 'Tones|Ringtones|Brazilian|Frevo',
4318            8083 => 'Tones|Ringtones|Brazilian|MPB',
4319            8084 => 'Tones|Ringtones|Brazilian|Pagode',
4320            8085 => 'Tones|Ringtones|Brazilian|Samba',
4321            8086 => 'Tones|Ringtones|Brazilian|Sertanejo',
4322            8087 => "Tones|Ringtones|Children's Music|Lullabies",
4323            8088 => "Tones|Ringtones|Children's Music|Sing-Along",
4324            8089 => "Tones|Ringtones|Children's Music|Stories",
4325            8090 => 'Tones|Ringtones|Chinese',
4326            8091 => 'Tones|Ringtones|Chinese|Chinese Classical',
4327            8092 => 'Tones|Ringtones|Chinese|Chinese Flute',
4328            8093 => 'Tones|Ringtones|Chinese|Chinese Opera',
4329            8094 => 'Tones|Ringtones|Chinese|Chinese Orchestral',
4330            8095 => 'Tones|Ringtones|Chinese|Chinese Regional Folk',
4331            8096 => 'Tones|Ringtones|Chinese|Chinese Strings',
4332            8097 => 'Tones|Ringtones|Chinese|Taiwanese Folk',
4333            8098 => 'Tones|Ringtones|Chinese|Tibetan Native Music',
4334            8099 => 'Tones|Ringtones|Christian & Gospel',
4335            8100 => 'Tones|Ringtones|Christian & Gospel|CCM',
4336            8101 => 'Tones|Ringtones|Christian & Gospel|Christian Metal',
4337            8102 => 'Tones|Ringtones|Christian & Gospel|Christian Pop',
4338            8103 => 'Tones|Ringtones|Christian & Gospel|Christian Rap',
4339            8104 => 'Tones|Ringtones|Christian & Gospel|Christian Rock',
4340            8105 => 'Tones|Ringtones|Christian & Gospel|Classic Christian',
4341            8106 => 'Tones|Ringtones|Christian & Gospel|Contemporary Gospel',
4342            8107 => 'Tones|Ringtones|Christian & Gospel|Gospel',
4343            8108 => 'Tones|Ringtones|Christian & Gospel|Praise & Worship',
4344            8109 => 'Tones|Ringtones|Christian & Gospel|Southern Gospel',
4345            8110 => 'Tones|Ringtones|Christian & Gospel|Traditional Gospel',
4346            8111 => 'Tones|Ringtones|Classical|Avant-Garde',
4347            8112 => 'Tones|Ringtones|Classical|Baroque Era',
4348            8113 => 'Tones|Ringtones|Classical|Chamber Music',
4349            8114 => 'Tones|Ringtones|Classical|Chant',
4350            8115 => 'Tones|Ringtones|Classical|Choral',
4351            8116 => 'Tones|Ringtones|Classical|Classical Crossover',
4352            8117 => 'Tones|Ringtones|Classical|Early Music',
4353            8118 => 'Tones|Ringtones|Classical|High Classical',
4354            8119 => 'Tones|Ringtones|Classical|Impressionist',
4355            8120 => 'Tones|Ringtones|Classical|Medieval Era',
4356            8121 => 'Tones|Ringtones|Classical|Minimalism',
4357            8122 => 'Tones|Ringtones|Classical|Modern Era',
4358            8123 => 'Tones|Ringtones|Classical|Orchestral',
4359            8124 => 'Tones|Ringtones|Classical|Renaissance',
4360            8125 => 'Tones|Ringtones|Classical|Romantic Era',
4361            8126 => 'Tones|Ringtones|Classical|Wedding Music',
4362            8127 => 'Tones|Ringtones|Comedy|Novelty',
4363            8128 => 'Tones|Ringtones|Comedy|Standup Comedy',
4364            8129 => 'Tones|Ringtones|Country|Alternative Country',
4365            8130 => 'Tones|Ringtones|Country|Americana',
4366            8131 => 'Tones|Ringtones|Country|Bluegrass',
4367            8132 => 'Tones|Ringtones|Country|Contemporary Bluegrass',
4368            8133 => 'Tones|Ringtones|Country|Contemporary Country',
4369            8134 => 'Tones|Ringtones|Country|Country Gospel',
4370            8135 => 'Tones|Ringtones|Country|Honky Tonk',
4371            8136 => 'Tones|Ringtones|Country|Outlaw Country',
4372            8137 => 'Tones|Ringtones|Country|Thai Country',
4373            8138 => 'Tones|Ringtones|Country|Traditional Bluegrass',
4374            8139 => 'Tones|Ringtones|Country|Traditional Country',
4375            8140 => 'Tones|Ringtones|Country|Urban Cowboy',
4376            8141 => 'Tones|Ringtones|Dance|Breakbeat',
4377            8142 => 'Tones|Ringtones|Dance|Exercise',
4378            8143 => 'Tones|Ringtones|Dance|Garage',
4379            8144 => 'Tones|Ringtones|Dance|Hardcore',
4380            8145 => 'Tones|Ringtones|Dance|House',
4381            8146 => "Tones|Ringtones|Dance|Jungle/Drum'n'bass",
4382            8147 => 'Tones|Ringtones|Dance|Techno',
4383            8148 => 'Tones|Ringtones|Dance|Trance',
4384            8149 => 'Tones|Ringtones|Disney',
4385            8150 => 'Tones|Ringtones|Easy Listening',
4386            8151 => 'Tones|Ringtones|Easy Listening|Lounge',
4387            8152 => 'Tones|Ringtones|Easy Listening|Swing',
4388            8153 => 'Tones|Ringtones|Electronic|Ambient',
4389            8154 => 'Tones|Ringtones|Electronic|Downtempo',
4390            8155 => 'Tones|Ringtones|Electronic|Electronica',
4391            8156 => 'Tones|Ringtones|Electronic|IDM/Experimental',
4392            8157 => 'Tones|Ringtones|Electronic|Industrial',
4393            8158 => 'Tones|Ringtones|Fitness & Workout',
4394            8159 => 'Tones|Ringtones|Folk',
4395            8160 => 'Tones|Ringtones|Hip-Hop/Rap|Alternative Rap',
4396            8161 => 'Tones|Ringtones|Hip-Hop/Rap|Chinese Hip-Hop',
4397            8162 => 'Tones|Ringtones|Hip-Hop/Rap|Dirty South',
4398            8163 => 'Tones|Ringtones|Hip-Hop/Rap|East Coast Rap',
4399            8164 => 'Tones|Ringtones|Hip-Hop/Rap|Gangsta Rap',
4400            8165 => 'Tones|Ringtones|Hip-Hop/Rap|Hardcore Rap',
4401            8166 => 'Tones|Ringtones|Hip-Hop/Rap|Hip-Hop',
4402            8167 => 'Tones|Ringtones|Hip-Hop/Rap|Korean Hip-Hop',
4403            8168 => 'Tones|Ringtones|Hip-Hop/Rap|Latin Rap',
4404            8169 => 'Tones|Ringtones|Hip-Hop/Rap|Old School Rap',
4405            8170 => 'Tones|Ringtones|Hip-Hop/Rap|Rap',
4406            8171 => 'Tones|Ringtones|Hip-Hop/Rap|Underground Rap',
4407            8172 => 'Tones|Ringtones|Hip-Hop/Rap|West Coast Rap',
4408            8173 => 'Tones|Ringtones|Holiday|Chanukah',
4409            8174 => 'Tones|Ringtones|Holiday|Christmas',
4410            8175 => "Tones|Ringtones|Holiday|Christmas: Children's",
4411            8176 => 'Tones|Ringtones|Holiday|Christmas: Classic',
4412            8177 => 'Tones|Ringtones|Holiday|Christmas: Classical',
4413            8178 => 'Tones|Ringtones|Holiday|Christmas: Jazz',
4414            8179 => 'Tones|Ringtones|Holiday|Christmas: Modern',
4415            8180 => 'Tones|Ringtones|Holiday|Christmas: Pop',
4416            8181 => 'Tones|Ringtones|Holiday|Christmas: R&B',
4417            8182 => 'Tones|Ringtones|Holiday|Christmas: Religious',
4418            8183 => 'Tones|Ringtones|Holiday|Christmas: Rock',
4419            8184 => 'Tones|Ringtones|Holiday|Easter',
4420            8185 => 'Tones|Ringtones|Holiday|Halloween',
4421            8186 => 'Tones|Ringtones|Holiday|Thanksgiving',
4422            8187 => 'Tones|Ringtones|Indian',
4423            8188 => 'Tones|Ringtones|Indian|Bollywood',
4424            8189 => 'Tones|Ringtones|Indian|Devotional & Spiritual',
4425            8190 => 'Tones|Ringtones|Indian|Ghazals',
4426            8191 => 'Tones|Ringtones|Indian|Indian Classical',
4427            8192 => 'Tones|Ringtones|Indian|Indian Folk',
4428            8193 => 'Tones|Ringtones|Indian|Indian Pop',
4429            8194 => 'Tones|Ringtones|Indian|Regional Indian',
4430            8195 => 'Tones|Ringtones|Indian|Sufi',
4431            8196 => 'Tones|Ringtones|Indian|Regional Indian|Tamil',
4432            8197 => 'Tones|Ringtones|Indian|Regional Indian|Telugu',
4433            8198 => 'Tones|Ringtones|Instrumental',
4434            8199 => 'Tones|Ringtones|Jazz|Avant-Garde Jazz',
4435            8201 => 'Tones|Ringtones|Jazz|Big Band',
4436            8202 => 'Tones|Ringtones|Jazz|Bop',
4437            8203 => 'Tones|Ringtones|Jazz|Contemporary Jazz',
4438            8204 => 'Tones|Ringtones|Jazz|Cool Jazz',
4439            8205 => 'Tones|Ringtones|Jazz|Crossover Jazz',
4440            8206 => 'Tones|Ringtones|Jazz|Dixieland',
4441            8207 => 'Tones|Ringtones|Jazz|Fusion',
4442            8208 => 'Tones|Ringtones|Jazz|Hard Bop',
4443            8209 => 'Tones|Ringtones|Jazz|Latin Jazz',
4444            8210 => 'Tones|Ringtones|Jazz|Mainstream Jazz',
4445            8211 => 'Tones|Ringtones|Jazz|Ragtime',
4446            8212 => 'Tones|Ringtones|Jazz|Smooth Jazz',
4447            8213 => 'Tones|Ringtones|Jazz|Trad Jazz',
4448            8214 => 'Tones|Ringtones|Pop|K-Pop',
4449            8215 => 'Tones|Ringtones|Karaoke',
4450            8216 => 'Tones|Ringtones|Korean',
4451            8217 => 'Tones|Ringtones|Korean|Korean Classical',
4452            8218 => 'Tones|Ringtones|Korean|Korean Trad Instrumental',
4453            8219 => 'Tones|Ringtones|Korean|Korean Trad Song',
4454            8220 => 'Tones|Ringtones|Korean|Korean Trad Theater',
4455            8221 => 'Tones|Ringtones|Latin|Alternative & Rock in Spanish',
4456            8222 => 'Tones|Ringtones|Latin|Baladas y Boleros',
4457            8223 => 'Tones|Ringtones|Latin|Contemporary Latin',
4458            8224 => 'Tones|Ringtones|Latin|Latin Jazz',
4459            8225 => 'Tones|Ringtones|Latin|Latin Urban',
4460            8226 => 'Tones|Ringtones|Latin|Pop in Spanish',
4461            8227 => 'Tones|Ringtones|Latin|Raices',
4462            8228 => 'Tones|Ringtones|Latin|Musica Mexicana', # (M&uacute;sica Mexicana)
4463            8229 => 'Tones|Ringtones|Latin|Salsa y Tropical',
4464            8230 => 'Tones|Ringtones|Marching Bands',
4465            8231 => 'Tones|Ringtones|New Age|Healing',
4466            8232 => 'Tones|Ringtones|New Age|Meditation',
4467            8233 => 'Tones|Ringtones|New Age|Nature',
4468            8234 => 'Tones|Ringtones|New Age|Relaxation',
4469            8235 => 'Tones|Ringtones|New Age|Travel',
4470            8236 => 'Tones|Ringtones|Orchestral',
4471            8237 => 'Tones|Ringtones|Pop|Adult Contemporary',
4472            8238 => 'Tones|Ringtones|Pop|Britpop',
4473            8239 => 'Tones|Ringtones|Pop|C-Pop',
4474            8240 => 'Tones|Ringtones|Pop|Cantopop/HK-Pop',
4475            8241 => 'Tones|Ringtones|Pop|Indo Pop',
4476            8242 => 'Tones|Ringtones|Pop|Korean Folk-Pop',
4477            8243 => 'Tones|Ringtones|Pop|Malaysian Pop',
4478            8244 => 'Tones|Ringtones|Pop|Mandopop',
4479            8245 => 'Tones|Ringtones|Pop|Manilla Sound',
4480            8246 => 'Tones|Ringtones|Pop|Oldies',
4481            8247 => 'Tones|Ringtones|Pop|Original Pilipino Music',
4482            8248 => 'Tones|Ringtones|Pop|Pinoy Pop',
4483            8249 => 'Tones|Ringtones|Pop|Pop/Rock',
4484            8250 => 'Tones|Ringtones|Pop|Soft Rock',
4485            8251 => 'Tones|Ringtones|Pop|Tai-Pop',
4486            8252 => 'Tones|Ringtones|Pop|Teen Pop',
4487            8253 => 'Tones|Ringtones|Pop|Thai Pop',
4488            8254 => 'Tones|Ringtones|R&B/Soul|Contemporary R&B',
4489            8255 => 'Tones|Ringtones|R&B/Soul|Disco',
4490            8256 => 'Tones|Ringtones|R&B/Soul|Doo Wop',
4491            8257 => 'Tones|Ringtones|R&B/Soul|Funk',
4492            8258 => 'Tones|Ringtones|R&B/Soul|Motown',
4493            8259 => 'Tones|Ringtones|R&B/Soul|Neo-Soul',
4494            8260 => 'Tones|Ringtones|R&B/Soul|Soul',
4495            8261 => 'Tones|Ringtones|Reggae|Modern Dancehall',
4496            8262 => 'Tones|Ringtones|Reggae|Dub',
4497            8263 => 'Tones|Ringtones|Reggae|Roots Reggae',
4498            8264 => 'Tones|Ringtones|Reggae|Ska',
4499            8265 => 'Tones|Ringtones|Rock|Adult Alternative',
4500            8266 => 'Tones|Ringtones|Rock|American Trad Rock',
4501            8267 => 'Tones|Ringtones|Rock|Arena Rock',
4502            8268 => 'Tones|Ringtones|Rock|Blues-Rock',
4503            8269 => 'Tones|Ringtones|Rock|British Invasion',
4504            8270 => 'Tones|Ringtones|Rock|Chinese Rock',
4505            8271 => 'Tones|Ringtones|Rock|Death Metal/Black Metal',
4506            8272 => 'Tones|Ringtones|Rock|Glam Rock',
4507            8273 => 'Tones|Ringtones|Rock|Hair Metal',
4508            8274 => 'Tones|Ringtones|Rock|Hard Rock',
4509            8275 => 'Tones|Ringtones|Rock|Metal',
4510            8276 => 'Tones|Ringtones|Rock|Jam Bands',
4511            8277 => 'Tones|Ringtones|Rock|Korean Rock',
4512            8278 => 'Tones|Ringtones|Rock|Prog-Rock/Art Rock',
4513            8279 => 'Tones|Ringtones|Rock|Psychedelic',
4514            8280 => 'Tones|Ringtones|Rock|Rock & Roll',
4515            8281 => 'Tones|Ringtones|Rock|Rockabilly',
4516            8282 => 'Tones|Ringtones|Rock|Roots Rock',
4517            8283 => 'Tones|Ringtones|Rock|Singer/Songwriter',
4518            8284 => 'Tones|Ringtones|Rock|Southern Rock',
4519            8285 => 'Tones|Ringtones|Rock|Surf',
4520            8286 => 'Tones|Ringtones|Rock|Tex-Mex',
4521            8287 => 'Tones|Ringtones|Singer/Songwriter|Alternative Folk',
4522            8288 => 'Tones|Ringtones|Singer/Songwriter|Contemporary Folk',
4523            8289 => 'Tones|Ringtones|Singer/Songwriter|Contemporary Singer/Songwriter',
4524            8290 => 'Tones|Ringtones|Singer/Songwriter|Folk-Rock',
4525            8291 => 'Tones|Ringtones|Singer/Songwriter|New Acoustic',
4526            8292 => 'Tones|Ringtones|Singer/Songwriter|Traditional Folk',
4527            8293 => 'Tones|Ringtones|Soundtrack|Foreign Cinema',
4528            8294 => 'Tones|Ringtones|Soundtrack|Musicals',
4529            8295 => 'Tones|Ringtones|Soundtrack|Original Score',
4530            8296 => 'Tones|Ringtones|Soundtrack|Sound Effects',
4531            8297 => 'Tones|Ringtones|Soundtrack|Soundtrack',
4532            8298 => 'Tones|Ringtones|Soundtrack|TV Soundtrack',
4533            8299 => 'Tones|Ringtones|Vocal|Standards',
4534            8300 => 'Tones|Ringtones|Vocal|Traditional Pop',
4535            8301 => 'Tones|Ringtones|Vocal|Trot',
4536            8302 => 'Tones|Ringtones|Jazz|Vocal Jazz',
4537            8303 => 'Tones|Ringtones|Vocal|Vocal Pop',
4538            8304 => 'Tones|Ringtones|African',
4539            8305 => 'Tones|Ringtones|African|Afrikaans',
4540            8306 => 'Tones|Ringtones|African|Afro-Beat',
4541            8307 => 'Tones|Ringtones|African|Afro-Pop',
4542            8308 => 'Tones|Ringtones|Turkish|Arabesque',
4543            8309 => 'Tones|Ringtones|World|Asia',
4544            8310 => 'Tones|Ringtones|World|Australia',
4545            8311 => 'Tones|Ringtones|World|Cajun',
4546            8312 => 'Tones|Ringtones|World|Calypso',
4547            8313 => 'Tones|Ringtones|World|Caribbean',
4548            8314 => 'Tones|Ringtones|World|Celtic',
4549            8315 => 'Tones|Ringtones|World|Celtic Folk',
4550            8316 => 'Tones|Ringtones|World|Contemporary Celtic',
4551            8317 => 'Tones|Ringtones|World|Dangdut',
4552            8318 => 'Tones|Ringtones|World|Dini',
4553            8319 => 'Tones|Ringtones|World|Europe',
4554            8320 => 'Tones|Ringtones|World|Fado',
4555            8321 => 'Tones|Ringtones|World|Farsi',
4556            8322 => 'Tones|Ringtones|World|Flamenco',
4557            8323 => 'Tones|Ringtones|World|France',
4558            8324 => 'Tones|Ringtones|Turkish|Halk',
4559            8325 => 'Tones|Ringtones|World|Hawaii',
4560            8326 => 'Tones|Ringtones|World|Iberia',
4561            8327 => 'Tones|Ringtones|World|Indonesian Religious',
4562            8328 => 'Tones|Ringtones|World|Israeli',
4563            8329 => 'Tones|Ringtones|World|Japan',
4564            8330 => 'Tones|Ringtones|World|Klezmer',
4565            8331 => 'Tones|Ringtones|World|North America',
4566            8332 => 'Tones|Ringtones|World|Polka',
4567            8333 => 'Tones|Ringtones|Russian',
4568            8334 => 'Tones|Ringtones|Russian|Russian Chanson',
4569            8335 => 'Tones|Ringtones|Turkish|Sanat',
4570            8336 => 'Tones|Ringtones|World|Soca',
4571            8337 => 'Tones|Ringtones|World|South Africa',
4572            8338 => 'Tones|Ringtones|World|South America',
4573            8339 => 'Tones|Ringtones|World|Tango',
4574            8340 => 'Tones|Ringtones|World|Traditional Celtic',
4575            8341 => 'Tones|Ringtones|Turkish',
4576            8342 => 'Tones|Ringtones|World|Worldbeat',
4577            8343 => 'Tones|Ringtones|World|Zydeco',
4578            8345 => 'Tones|Ringtones|Classical|Art Song',
4579            8346 => 'Tones|Ringtones|Classical|Brass & Woodwinds',
4580            8347 => 'Tones|Ringtones|Classical|Solo Instrumental',
4581            8348 => 'Tones|Ringtones|Classical|Contemporary Era',
4582            8349 => 'Tones|Ringtones|Classical|Oratorio',
4583            8350 => 'Tones|Ringtones|Classical|Cantata',
4584            8351 => 'Tones|Ringtones|Classical|Electronic',
4585            8352 => 'Tones|Ringtones|Classical|Sacred',
4586            8353 => 'Tones|Ringtones|Classical|Guitar',
4587            8354 => 'Tones|Ringtones|Classical|Piano',
4588            8355 => 'Tones|Ringtones|Classical|Violin',
4589            8356 => 'Tones|Ringtones|Classical|Cello',
4590            8357 => 'Tones|Ringtones|Classical|Percussion',
4591            8358 => 'Tones|Ringtones|Electronic|Dubstep',
4592            8359 => 'Tones|Ringtones|Electronic|Bass',
4593            8360 => 'Tones|Ringtones|Hip-Hop/Rap|UK Hip Hop',
4594            8361 => 'Tones|Ringtones|Reggae|Lovers Rock',
4595            8362 => 'Tones|Ringtones|Alternative|EMO',
4596            8363 => 'Tones|Ringtones|Alternative|Pop Punk',
4597            8364 => 'Tones|Ringtones|Alternative|Indie Pop',
4598            8365 => 'Tones|Ringtones|New Age|Yoga',
4599            8366 => 'Tones|Ringtones|Pop|Tribute',
4600            8367 => 'Tones|Ringtones|Pop|Shows',
4601            8368 => 'Tones|Ringtones|Cuban',
4602            8369 => 'Tones|Ringtones|Cuban|Mambo',
4603            8370 => 'Tones|Ringtones|Cuban|Chachacha',
4604            8371 => 'Tones|Ringtones|Cuban|Guajira',
4605            8372 => 'Tones|Ringtones|Cuban|Son',
4606            8373 => 'Tones|Ringtones|Cuban|Bolero',
4607            8374 => 'Tones|Ringtones|Cuban|Guaracha',
4608            8375 => 'Tones|Ringtones|Cuban|Timba',
4609            8376 => 'Tones|Ringtones|Soundtrack|Video Game',
4610            8377 => 'Tones|Ringtones|Indian|Regional Indian|Punjabi|Punjabi Pop',
4611            8378 => 'Tones|Ringtones|Indian|Regional Indian|Bengali|Rabindra Sangeet',
4612            8379 => 'Tones|Ringtones|Indian|Regional Indian|Malayalam',
4613            8380 => 'Tones|Ringtones|Indian|Regional Indian|Kannada',
4614            8381 => 'Tones|Ringtones|Indian|Regional Indian|Marathi',
4615            8382 => 'Tones|Ringtones|Indian|Regional Indian|Gujarati',
4616            8383 => 'Tones|Ringtones|Indian|Regional Indian|Assamese',
4617            8384 => 'Tones|Ringtones|Indian|Regional Indian|Bhojpuri',
4618            8385 => 'Tones|Ringtones|Indian|Regional Indian|Haryanvi',
4619            8386 => 'Tones|Ringtones|Indian|Regional Indian|Odia',
4620            8387 => 'Tones|Ringtones|Indian|Regional Indian|Rajasthani',
4621            8388 => 'Tones|Ringtones|Indian|Regional Indian|Urdu',
4622            8389 => 'Tones|Ringtones|Indian|Regional Indian|Punjabi',
4623            8390 => 'Tones|Ringtones|Indian|Regional Indian|Bengali',
4624            8391 => 'Tones|Ringtones|Indian|Indian Classical|Carnatic Classical',
4625            8392 => 'Tones|Ringtones|Indian|Indian Classical|Hindustani Classical',
4626            8393 => 'Tones|Ringtones|African|Afro House',
4627            8394 => 'Tones|Ringtones|African|Afro Soul',
4628            8395 => 'Tones|Ringtones|African|Afrobeats',
4629            8396 => 'Tones|Ringtones|African|Benga',
4630            8397 => 'Tones|Ringtones|African|Bongo-Flava',
4631            8398 => 'Tones|Ringtones|African|Coupe-Decale',
4632            8399 => 'Tones|Ringtones|African|Gqom',
4633            8400 => 'Tones|Ringtones|African|Highlife',
4634            8401 => 'Tones|Ringtones|African|Kuduro',
4635            8402 => 'Tones|Ringtones|African|Kizomba',
4636            8403 => 'Tones|Ringtones|African|Kwaito',
4637            8404 => 'Tones|Ringtones|African|Mbalax',
4638            8405 => 'Tones|Ringtones|African|Ndombolo',
4639            8406 => 'Tones|Ringtones|African|Shangaan Electro',
4640            8407 => 'Tones|Ringtones|African|Soukous',
4641            8408 => 'Tones|Ringtones|African|Taarab',
4642            8409 => 'Tones|Ringtones|African|Zouglou',
4643            8410 => 'Tones|Ringtones|Turkish|Ozgun',
4644            8411 => 'Tones|Ringtones|Turkish|Fantezi',
4645            8412 => 'Tones|Ringtones|Turkish|Religious',
4646            8413 => 'Tones|Ringtones|Pop|Turkish Pop',
4647            8414 => 'Tones|Ringtones|Rock|Turkish Rock',
4648            8415 => 'Tones|Ringtones|Alternative|Turkish Alternative',
4649            8416 => 'Tones|Ringtones|Hip-Hop/Rap|Turkish Hip-Hop/Rap',
4650            8417 => 'Tones|Ringtones|African|Maskandi',
4651            8418 => 'Tones|Ringtones|Russian|Russian Romance',
4652            8419 => 'Tones|Ringtones|Russian|Russian Bard',
4653            8420 => 'Tones|Ringtones|Russian|Russian Pop',
4654            8421 => 'Tones|Ringtones|Russian|Russian Rock',
4655            8422 => 'Tones|Ringtones|Russian|Russian Hip-Hop',
4656            8423 => 'Tones|Ringtones|Arabic|Levant',
4657            8424 => 'Tones|Ringtones|Arabic|Levant|Dabke',
4658            8425 => 'Tones|Ringtones|Arabic|Maghreb Rai',
4659            8426 => 'Tones|Ringtones|Arabic|Khaleeji|Khaleeji Jalsat',
4660            8427 => 'Tones|Ringtones|Arabic|Khaleeji|Khaleeji Shailat',
4661            8428 => 'Tones|Ringtones|Tarab',
4662            8429 => 'Tones|Ringtones|Tarab|Iraqi Tarab',
4663            8430 => 'Tones|Ringtones|Tarab|Egyptian Tarab',
4664            8431 => 'Tones|Ringtones|Tarab|Khaleeji Tarab',
4665            8432 => 'Tones|Ringtones|Pop|Levant Pop',
4666            8433 => 'Tones|Ringtones|Pop|Iraqi Pop',
4667            8434 => 'Tones|Ringtones|Pop|Egyptian Pop',
4668            8435 => 'Tones|Ringtones|Pop|Maghreb Pop',
4669            8436 => 'Tones|Ringtones|Pop|Khaleeji Pop',
4670            8437 => 'Tones|Ringtones|Hip-Hop/Rap|Levant Hip-Hop',
4671            8438 => 'Tones|Ringtones|Hip-Hop/Rap|Egyptian Hip-Hop',
4672            8439 => 'Tones|Ringtones|Hip-Hop/Rap|Maghreb Hip-Hop',
4673            8440 => 'Tones|Ringtones|Hip-Hop/Rap|Khaleeji Hip-Hop',
4674            8441 => 'Tones|Ringtones|Alternative|Indie Levant',
4675            8442 => 'Tones|Ringtones|Alternative|Indie Egyptian',
4676            8443 => 'Tones|Ringtones|Alternative|Indie Maghreb',
4677            8444 => 'Tones|Ringtones|Electronic|Levant Electronic',
4678            8445 => "Tones|Ringtones|Electronic|Electro-Cha'abi",
4679            8446 => 'Tones|Ringtones|Electronic|Maghreb Electronic',
4680            8447 => 'Tones|Ringtones|Folk|Iraqi Folk',
4681            8448 => 'Tones|Ringtones|Folk|Khaleeji Folk',
4682            8449 => 'Tones|Ringtones|Dance|Maghreb Dance',
4683            9002 => 'Books|Nonfiction',
4684            9003 => 'Books|Romance',
4685            9004 => 'Books|Travel & Adventure',
4686            9007 => 'Books|Arts & Entertainment',
4687            9008 => 'Books|Biographies & Memoirs',
4688            9009 => 'Books|Business & Personal Finance',
4689            9010 => 'Books|Children & Teens',
4690            9012 => 'Books|Humor',
4691            9015 => 'Books|History',
4692            9018 => 'Books|Religion & Spirituality',
4693            9019 => 'Books|Science & Nature',
4694            9020 => 'Books|Sci-Fi & Fantasy',
4695            9024 => 'Books|Lifestyle & Home',
4696            9025 => 'Books|Self-Development',
4697            9026 => 'Books|Comics & Graphic Novels',
4698            9027 => 'Books|Computers & Internet',
4699            9028 => 'Books|Cookbooks, Food & Wine',
4700            9029 => 'Books|Professional & Technical',
4701            9030 => 'Books|Parenting',
4702            9031 => 'Books|Fiction & Literature',
4703            9032 => 'Books|Mysteries & Thrillers',
4704            9033 => 'Books|Reference',
4705            9034 => 'Books|Politics & Current Events',
4706            9035 => 'Books|Sports & Outdoors',
4707            10001 => 'Books|Lifestyle & Home|Antiques & Collectibles',
4708            10002 => 'Books|Arts & Entertainment|Art & Architecture',
4709            10003 => 'Books|Religion & Spirituality|Bibles',
4710            10004 => 'Books|Self-Development|Spirituality',
4711            10005 => 'Books|Business & Personal Finance|Industries & Professions',
4712            10006 => 'Books|Business & Personal Finance|Marketing & Sales',
4713            10007 => 'Books|Business & Personal Finance|Small Business & Entrepreneurship',
4714            10008 => 'Books|Business & Personal Finance|Personal Finance',
4715            10009 => 'Books|Business & Personal Finance|Reference',
4716            10010 => 'Books|Business & Personal Finance|Careers',
4717            10011 => 'Books|Business & Personal Finance|Economics',
4718            10012 => 'Books|Business & Personal Finance|Investing',
4719            10013 => 'Books|Business & Personal Finance|Finance',
4720            10014 => 'Books|Business & Personal Finance|Management & Leadership',
4721            10015 => 'Books|Comics & Graphic Novels|Graphic Novels',
4722            10016 => 'Books|Comics & Graphic Novels|Manga',
4723            10017 => 'Books|Computers & Internet|Computers',
4724            10018 => 'Books|Computers & Internet|Databases',
4725            10019 => 'Books|Computers & Internet|Digital Media',
4726            10020 => 'Books|Computers & Internet|Internet',
4727            10021 => 'Books|Computers & Internet|Network',
4728            10022 => 'Books|Computers & Internet|Operating Systems',
4729            10023 => 'Books|Computers & Internet|Programming',
4730            10024 => 'Books|Computers & Internet|Software',
4731            10025 => 'Books|Computers & Internet|System Administration',
4732            10026 => 'Books|Cookbooks, Food & Wine|Beverages',
4733            10027 => 'Books|Cookbooks, Food & Wine|Courses & Dishes',
4734            10028 => 'Books|Cookbooks, Food & Wine|Special Diet',
4735            10029 => 'Books|Cookbooks, Food & Wine|Special Occasions',
4736            10030 => 'Books|Cookbooks, Food & Wine|Methods',
4737            10031 => 'Books|Cookbooks, Food & Wine|Reference',
4738            10032 => 'Books|Cookbooks, Food & Wine|Regional & Ethnic',
4739            10033 => 'Books|Cookbooks, Food & Wine|Specific Ingredients',
4740            10034 => 'Books|Lifestyle & Home|Crafts & Hobbies',
4741            10035 => 'Books|Professional & Technical|Design',
4742            10036 => 'Books|Arts & Entertainment|Theater',
4743            10037 => 'Books|Professional & Technical|Education',
4744            10038 => 'Books|Nonfiction|Family & Relationships',
4745            10039 => 'Books|Fiction & Literature|Action & Adventure',
4746            10040 => 'Books|Fiction & Literature|African American',
4747            10041 => 'Books|Fiction & Literature|Religious',
4748            10042 => 'Books|Fiction & Literature|Classics',
4749            10043 => 'Books|Fiction & Literature|Erotica',
4750            10044 => 'Books|Sci-Fi & Fantasy|Fantasy',
4751            10045 => 'Books|Fiction & Literature|Gay',
4752            10046 => 'Books|Fiction & Literature|Ghost',
4753            10047 => 'Books|Fiction & Literature|Historical',
4754            10048 => 'Books|Fiction & Literature|Horror',
4755            10049 => 'Books|Fiction & Literature|Literary',
4756            10050 => 'Books|Mysteries & Thrillers|Hard-Boiled',
4757            10051 => 'Books|Mysteries & Thrillers|Historical',
4758            10052 => 'Books|Mysteries & Thrillers|Police Procedural',
4759            10053 => 'Books|Mysteries & Thrillers|Short Stories',
4760            10054 => 'Books|Mysteries & Thrillers|British Detectives',
4761            10055 => 'Books|Mysteries & Thrillers|Women Sleuths',
4762            10056 => 'Books|Romance|Erotic Romance',
4763            10057 => 'Books|Romance|Contemporary',
4764            10058 => 'Books|Romance|Paranormal',
4765            10059 => 'Books|Romance|Historical',
4766            10060 => 'Books|Romance|Short Stories',
4767            10061 => 'Books|Romance|Suspense',
4768            10062 => 'Books|Romance|Western',
4769            10063 => 'Books|Sci-Fi & Fantasy|Science Fiction',
4770            10064 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature',
4771            10065 => 'Books|Fiction & Literature|Short Stories',
4772            10066 => 'Books|Reference|Foreign Languages',
4773            10067 => 'Books|Arts & Entertainment|Games',
4774            10068 => 'Books|Lifestyle & Home|Gardening',
4775            10069 => 'Books|Self-Development|Health & Fitness',
4776            10070 => 'Books|History|Africa',
4777            10071 => 'Books|History|Americas',
4778            10072 => 'Books|History|Ancient',
4779            10073 => 'Books|History|Asia',
4780            10074 => 'Books|History|Australia & Oceania',
4781            10075 => 'Books|History|Europe',
4782            10076 => 'Books|History|Latin America',
4783            10077 => 'Books|History|Middle East',
4784            10078 => 'Books|History|Military',
4785            10079 => 'Books|History|United States',
4786            10080 => 'Books|History|World',
4787            10081 => "Books|Children & Teens|Children's Fiction",
4788            10082 => "Books|Children & Teens|Children's Nonfiction",
4789            10083 => 'Books|Professional & Technical|Law',
4790            10084 => 'Books|Fiction & Literature|Literary Criticism',
4791            10085 => 'Books|Science & Nature|Mathematics',
4792            10086 => 'Books|Professional & Technical|Medical',
4793            10087 => 'Books|Arts & Entertainment|Music',
4794            10088 => 'Books|Science & Nature|Nature',
4795            10089 => 'Books|Arts & Entertainment|Performing Arts',
4796            10090 => 'Books|Lifestyle & Home|Pets',
4797            10091 => 'Books|Nonfiction|Philosophy',
4798            10092 => 'Books|Arts & Entertainment|Photography',
4799            10093 => 'Books|Fiction & Literature|Poetry',
4800            10094 => 'Books|Self-Development|Psychology',
4801            10095 => 'Books|Reference|Almanacs & Yearbooks',
4802            10096 => 'Books|Reference|Atlases & Maps',
4803            10097 => 'Books|Reference|Catalogs & Directories',
4804            10098 => 'Books|Reference|Consumer Guides',
4805            10099 => 'Books|Reference|Dictionaries & Thesauruses',
4806            10100 => 'Books|Reference|Encyclopedias',
4807            10101 => 'Books|Reference|Etiquette',
4808            10102 => 'Books|Reference|Quotations',
4809            10103 => 'Books|Reference|Words & Language',
4810            10104 => 'Books|Reference|Writing',
4811            10105 => 'Books|Religion & Spirituality|Bible Studies',
4812            10106 => 'Books|Religion & Spirituality|Buddhism',
4813            10107 => 'Books|Religion & Spirituality|Christianity',
4814            10108 => 'Books|Religion & Spirituality|Hinduism',
4815            10109 => 'Books|Religion & Spirituality|Islam',
4816            10110 => 'Books|Religion & Spirituality|Judaism',
4817            10111 => 'Books|Science & Nature|Astronomy',
4818            10112 => 'Books|Science & Nature|Chemistry',
4819            10113 => 'Books|Science & Nature|Earth Sciences',
4820            10114 => 'Books|Science & Nature|Essays',
4821            10115 => 'Books|Science & Nature|History',
4822            10116 => 'Books|Science & Nature|Life Sciences',
4823            10117 => 'Books|Science & Nature|Physics',
4824            10118 => 'Books|Science & Nature|Reference',
4825            10119 => 'Books|Self-Development|Self-Improvement',
4826            10120 => 'Books|Nonfiction|Social Science',
4827            10121 => 'Books|Sports & Outdoors|Baseball',
4828            10122 => 'Books|Sports & Outdoors|Basketball',
4829            10123 => 'Books|Sports & Outdoors|Coaching',
4830            10124 => 'Books|Sports & Outdoors|Extreme Sports',
4831            10125 => 'Books|Sports & Outdoors|Football',
4832            10126 => 'Books|Sports & Outdoors|Golf',
4833            10127 => 'Books|Sports & Outdoors|Hockey',
4834            10128 => 'Books|Sports & Outdoors|Mountaineering',
4835            10129 => 'Books|Sports & Outdoors|Outdoors',
4836            10130 => 'Books|Sports & Outdoors|Racket Sports',
4837            10131 => 'Books|Sports & Outdoors|Reference',
4838            10132 => 'Books|Sports & Outdoors|Soccer',
4839            10133 => 'Books|Sports & Outdoors|Training',
4840            10134 => 'Books|Sports & Outdoors|Water Sports',
4841            10135 => 'Books|Sports & Outdoors|Winter Sports',
4842            10136 => 'Books|Reference|Study Aids',
4843            10137 => 'Books|Professional & Technical|Engineering',
4844            10138 => 'Books|Nonfiction|Transportation',
4845            10139 => 'Books|Travel & Adventure|Africa',
4846            10140 => 'Books|Travel & Adventure|Asia',
4847            10141 => 'Books|Travel & Adventure|Specialty Travel',
4848            10142 => 'Books|Travel & Adventure|Canada',
4849            10143 => 'Books|Travel & Adventure|Caribbean',
4850            10144 => 'Books|Travel & Adventure|Latin America',
4851            10145 => 'Books|Travel & Adventure|Essays & Memoirs',
4852            10146 => 'Books|Travel & Adventure|Europe',
4853            10147 => 'Books|Travel & Adventure|Middle East',
4854            10148 => 'Books|Travel & Adventure|United States',
4855            10149 => 'Books|Nonfiction|True Crime',
4856            11001 => 'Books|Sci-Fi & Fantasy|Fantasy|Contemporary',
4857            11002 => 'Books|Sci-Fi & Fantasy|Fantasy|Epic',
4858            11003 => 'Books|Sci-Fi & Fantasy|Fantasy|Historical',
4859            11004 => 'Books|Sci-Fi & Fantasy|Fantasy|Paranormal',
4860            11005 => 'Books|Sci-Fi & Fantasy|Fantasy|Short Stories',
4861            11006 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature|Adventure',
4862            11007 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature|High Tech',
4863            11008 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature|Short Stories',
4864            11009 => 'Books|Professional & Technical|Education|Language Arts & Disciplines',
4865            11010 => 'Books|Communications & Media',
4866            11011 => 'Books|Communications & Media|Broadcasting',
4867            11012 => 'Books|Communications & Media|Digital Media',
4868            11013 => 'Books|Communications & Media|Journalism',
4869            11014 => 'Books|Communications & Media|Photojournalism',
4870            11015 => 'Books|Communications & Media|Print',
4871            11016 => 'Books|Communications & Media|Speech',
4872            11017 => 'Books|Communications & Media|Writing',
4873            11018 => 'Books|Arts & Entertainment|Art & Architecture|Urban Planning',
4874            11019 => 'Books|Arts & Entertainment|Dance',
4875            11020 => 'Books|Arts & Entertainment|Fashion',
4876            11021 => 'Books|Arts & Entertainment|Film',
4877            11022 => 'Books|Arts & Entertainment|Interior Design',
4878            11023 => 'Books|Arts & Entertainment|Media Arts',
4879            11024 => 'Books|Arts & Entertainment|Radio',
4880            11025 => 'Books|Arts & Entertainment|TV',
4881            11026 => 'Books|Arts & Entertainment|Visual Arts',
4882            11027 => 'Books|Biographies & Memoirs|Arts & Entertainment',
4883            11028 => 'Books|Biographies & Memoirs|Business',
4884            11029 => 'Books|Biographies & Memoirs|Culinary',
4885            11030 => 'Books|Biographies & Memoirs|Gay & Lesbian',
4886            11031 => 'Books|Biographies & Memoirs|Historical',
4887            11032 => 'Books|Biographies & Memoirs|Literary',
4888            11033 => 'Books|Biographies & Memoirs|Media & Journalism',
4889            11034 => 'Books|Biographies & Memoirs|Military',
4890            11035 => 'Books|Biographies & Memoirs|Politics',
4891            11036 => 'Books|Biographies & Memoirs|Religious',
4892            11037 => 'Books|Biographies & Memoirs|Science & Technology',
4893            11038 => 'Books|Biographies & Memoirs|Sports',
4894            11039 => 'Books|Biographies & Memoirs|Women',
4895            11040 => 'Books|Romance|New Adult',
4896            11042 => 'Books|Romance|Romantic Comedy',
4897            11043 => 'Books|Romance|Gay & Lesbian',
4898            11044 => 'Books|Fiction & Literature|Essays',
4899            11045 => 'Books|Fiction & Literature|Anthologies',
4900            11046 => 'Books|Fiction & Literature|Comparative Literature',
4901            11047 => 'Books|Fiction & Literature|Drama',
4902            11049 => 'Books|Fiction & Literature|Fairy Tales, Myths & Fables',
4903            11050 => 'Books|Fiction & Literature|Family',
4904            11051 => 'Books|Comics & Graphic Novels|Manga|School Drama',
4905            11052 => 'Books|Comics & Graphic Novels|Manga|Human Drama',
4906            11053 => 'Books|Comics & Graphic Novels|Manga|Family Drama',
4907            11054 => 'Books|Sports & Outdoors|Boxing',
4908            11055 => 'Books|Sports & Outdoors|Cricket',
4909            11056 => 'Books|Sports & Outdoors|Cycling',
4910            11057 => 'Books|Sports & Outdoors|Equestrian',
4911            11058 => 'Books|Sports & Outdoors|Martial Arts & Self Defense',
4912            11059 => 'Books|Sports & Outdoors|Motor Sports',
4913            11060 => 'Books|Sports & Outdoors|Rugby',
4914            11061 => 'Books|Sports & Outdoors|Running',
4915            11062 => 'Books|Self-Development|Diet & Nutrition',
4916            11063 => 'Books|Science & Nature|Agriculture',
4917            11064 => 'Books|Science & Nature|Atmosphere',
4918            11065 => 'Books|Science & Nature|Biology',
4919            11066 => 'Books|Science & Nature|Ecology',
4920            11067 => 'Books|Science & Nature|Environment',
4921            11068 => 'Books|Science & Nature|Geography',
4922            11069 => 'Books|Science & Nature|Geology',
4923            11070 => 'Books|Nonfiction|Social Science|Anthropology',
4924            11071 => 'Books|Nonfiction|Social Science|Archaeology',
4925            11072 => 'Books|Nonfiction|Social Science|Civics',
4926            11073 => 'Books|Nonfiction|Social Science|Government',
4927            11074 => 'Books|Nonfiction|Social Science|Social Studies',
4928            11075 => 'Books|Nonfiction|Social Science|Social Welfare',
4929            11076 => 'Books|Nonfiction|Social Science|Society',
4930            11077 => 'Books|Nonfiction|Philosophy|Aesthetics',
4931            11078 => 'Books|Nonfiction|Philosophy|Epistemology',
4932            11079 => 'Books|Nonfiction|Philosophy|Ethics',
4933            11080 => 'Books|Nonfiction|Philosophy|Language',
4934            11081 => 'Books|Nonfiction|Philosophy|Logic',
4935            11082 => 'Books|Nonfiction|Philosophy|Metaphysics',
4936            11083 => 'Books|Nonfiction|Philosophy|Political',
4937            11084 => 'Books|Nonfiction|Philosophy|Religion',
4938            11085 => 'Books|Reference|Manuals',
4939            11086 => 'Books|Kids',
4940            11087 => 'Books|Kids|Animals',
4941            11088 => 'Books|Kids|Basic Concepts',
4942            11089 => 'Books|Kids|Basic Concepts|Alphabet',
4943            11090 => 'Books|Kids|Basic Concepts|Body',
4944            11091 => 'Books|Kids|Basic Concepts|Colors',
4945            11092 => 'Books|Kids|Basic Concepts|Counting & Numbers',
4946            11093 => 'Books|Kids|Basic Concepts|Date & Time',
4947            11094 => 'Books|Kids|Basic Concepts|General',
4948            11095 => 'Books|Kids|Basic Concepts|Money',
4949            11096 => 'Books|Kids|Basic Concepts|Opposites',
4950            11097 => 'Books|Kids|Basic Concepts|Seasons',
4951            11098 => 'Books|Kids|Basic Concepts|Senses & Sensation',
4952            11099 => 'Books|Kids|Basic Concepts|Size & Shape',
4953            11100 => 'Books|Kids|Basic Concepts|Sounds',
4954            11101 => 'Books|Kids|Basic Concepts|Words',
4955            11102 => 'Books|Kids|Biography',
4956            11103 => 'Books|Kids|Careers & Occupations',
4957            11104 => 'Books|Kids|Computers & Technology',
4958            11105 => 'Books|Kids|Cooking & Food',
4959            11106 => 'Books|Kids|Arts & Entertainment',
4960            11107 => 'Books|Kids|Arts & Entertainment|Art',
4961            11108 => 'Books|Kids|Arts & Entertainment|Crafts',
4962            11109 => 'Books|Kids|Arts & Entertainment|Music',
4963            11110 => 'Books|Kids|Arts & Entertainment|Performing Arts',
4964            11111 => 'Books|Kids|Family',
4965            11112 => 'Books|Kids|Fiction',
4966            11113 => 'Books|Kids|Fiction|Action & Adventure',
4967            11114 => 'Books|Kids|Fiction|Animals',
4968            11115 => 'Books|Kids|Fiction|Classics',
4969            11116 => 'Books|Kids|Fiction|Comics & Graphic Novels',
4970            11117 => 'Books|Kids|Fiction|Culture, Places & People',
4971            11118 => 'Books|Kids|Fiction|Family & Relationships',
4972            11119 => 'Books|Kids|Fiction|Fantasy',
4973            11120 => 'Books|Kids|Fiction|Fairy Tales, Myths & Fables',
4974            11121 => 'Books|Kids|Fiction|Favorite Characters',
4975            11122 => 'Books|Kids|Fiction|Historical',
4976            11123 => 'Books|Kids|Fiction|Holidays & Celebrations',
4977            11124 => 'Books|Kids|Fiction|Monsters & Ghosts',
4978            11125 => 'Books|Kids|Fiction|Mysteries',
4979            11126 => 'Books|Kids|Fiction|Nature',
4980            11127 => 'Books|Kids|Fiction|Religion',
4981            11128 => 'Books|Kids|Fiction|Sci-Fi',
4982            11129 => 'Books|Kids|Fiction|Social Issues',
4983            11130 => 'Books|Kids|Fiction|Sports & Recreation',
4984            11131 => 'Books|Kids|Fiction|Transportation',
4985            11132 => 'Books|Kids|Games & Activities',
4986            11133 => 'Books|Kids|General Nonfiction',
4987            11134 => 'Books|Kids|Health',
4988            11135 => 'Books|Kids|History',
4989            11136 => 'Books|Kids|Holidays & Celebrations',
4990            11137 => 'Books|Kids|Holidays & Celebrations|Birthdays',
4991            11138 => 'Books|Kids|Holidays & Celebrations|Christmas & Advent',
4992            11139 => 'Books|Kids|Holidays & Celebrations|Easter & Lent',
4993            11140 => 'Books|Kids|Holidays & Celebrations|General',
4994            11141 => 'Books|Kids|Holidays & Celebrations|Halloween',
4995            11142 => 'Books|Kids|Holidays & Celebrations|Hanukkah',
4996            11143 => 'Books|Kids|Holidays & Celebrations|Other',
4997            11144 => 'Books|Kids|Holidays & Celebrations|Passover',
4998            11145 => 'Books|Kids|Holidays & Celebrations|Patriotic Holidays',
4999            11146 => 'Books|Kids|Holidays & Celebrations|Ramadan',
5000            11147 => 'Books|Kids|Holidays & Celebrations|Thanksgiving',
5001            11148 => "Books|Kids|Holidays & Celebrations|Valentine's Day",
5002            11149 => 'Books|Kids|Humor',
5003            11150 => 'Books|Kids|Humor|Jokes & Riddles',
5004            11151 => 'Books|Kids|Poetry',
5005            11152 => 'Books|Kids|Learning to Read',
5006            11153 => 'Books|Kids|Learning to Read|Chapter Books',
5007            11154 => 'Books|Kids|Learning to Read|Early Readers',
5008            11155 => 'Books|Kids|Learning to Read|Intermediate Readers',
5009            11156 => 'Books|Kids|Nursery Rhymes',
5010            11157 => 'Books|Kids|Government',
5011            11158 => 'Books|Kids|Reference',
5012            11159 => 'Books|Kids|Religion',
5013            11160 => 'Books|Kids|Science & Nature',
5014            11161 => 'Books|Kids|Social Issues',
5015            11162 => 'Books|Kids|Social Studies',
5016            11163 => 'Books|Kids|Sports & Recreation',
5017            11164 => 'Books|Kids|Transportation',
5018            11165 => 'Books|Young Adult',
5019            11166 => 'Books|Young Adult|Animals',
5020            11167 => 'Books|Young Adult|Biography',
5021            11168 => 'Books|Young Adult|Careers & Occupations',
5022            11169 => 'Books|Young Adult|Computers & Technology',
5023            11170 => 'Books|Young Adult|Cooking & Food',
5024            11171 => 'Books|Young Adult|Arts & Entertainment',
5025            11172 => 'Books|Young Adult|Arts & Entertainment|Art',
5026            11173 => 'Books|Young Adult|Arts & Entertainment|Crafts',
5027            11174 => 'Books|Young Adult|Arts & Entertainment|Music',
5028            11175 => 'Books|Young Adult|Arts & Entertainment|Performing Arts',
5029            11176 => 'Books|Young Adult|Family',
5030            11177 => 'Books|Young Adult|Fiction',
5031            11178 => 'Books|Young Adult|Fiction|Action & Adventure',
5032            11179 => 'Books|Young Adult|Fiction|Animals',
5033            11180 => 'Books|Young Adult|Fiction|Classics',
5034            11181 => 'Books|Young Adult|Fiction|Comics & Graphic Novels',
5035            11182 => 'Books|Young Adult|Fiction|Culture, Places & People',
5036            11183 => 'Books|Young Adult|Fiction|Dystopian',
5037            11184 => 'Books|Young Adult|Fiction|Family & Relationships',
5038            11185 => 'Books|Young Adult|Fiction|Fantasy',
5039            11186 => 'Books|Young Adult|Fiction|Fairy Tales, Myths & Fables',
5040            11187 => 'Books|Young Adult|Fiction|Favorite Characters',
5041            11188 => 'Books|Young Adult|Fiction|Historical',
5042            11189 => 'Books|Young Adult|Fiction|Holidays & Celebrations',
5043            11190 => 'Books|Young Adult|Fiction|Horror, Monsters & Ghosts',
5044            11191 => 'Books|Young Adult|Fiction|Crime & Mystery',
5045            11192 => 'Books|Young Adult|Fiction|Nature',
5046            11193 => 'Books|Young Adult|Fiction|Religion',
5047            11194 => 'Books|Young Adult|Fiction|Romance',
5048            11195 => 'Books|Young Adult|Fiction|Sci-Fi',
5049            11196 => 'Books|Young Adult|Fiction|Coming of Age',
5050            11197 => 'Books|Young Adult|Fiction|Sports & Recreation',
5051            11198 => 'Books|Young Adult|Fiction|Transportation',
5052            11199 => 'Books|Young Adult|Games & Activities',
5053            11200 => 'Books|Young Adult|General Nonfiction',
5054            11201 => 'Books|Young Adult|Health',
5055            11202 => 'Books|Young Adult|History',
5056            11203 => 'Books|Young Adult|Holidays & Celebrations',
5057            11204 => 'Books|Young Adult|Holidays & Celebrations|Birthdays',
5058            11205 => 'Books|Young Adult|Holidays & Celebrations|Christmas & Advent',
5059            11206 => 'Books|Young Adult|Holidays & Celebrations|Easter & Lent',
5060            11207 => 'Books|Young Adult|Holidays & Celebrations|General',
5061            11208 => 'Books|Young Adult|Holidays & Celebrations|Halloween',
5062            11209 => 'Books|Young Adult|Holidays & Celebrations|Hanukkah',
5063            11210 => 'Books|Young Adult|Holidays & Celebrations|Other',
5064            11211 => 'Books|Young Adult|Holidays & Celebrations|Passover',
5065            11212 => 'Books|Young Adult|Holidays & Celebrations|Patriotic Holidays',
5066            11213 => 'Books|Young Adult|Holidays & Celebrations|Ramadan',
5067            11214 => 'Books|Young Adult|Holidays & Celebrations|Thanksgiving',
5068            11215 => "Books|Young Adult|Holidays & Celebrations|Valentine's Day",
5069            11216 => 'Books|Young Adult|Humor',
5070            11217 => 'Books|Young Adult|Humor|Jokes & Riddles',
5071            11218 => 'Books|Young Adult|Poetry',
5072            11219 => 'Books|Young Adult|Politics & Government',
5073            11220 => 'Books|Young Adult|Reference',
5074            11221 => 'Books|Young Adult|Religion',
5075            11222 => 'Books|Young Adult|Science & Nature',
5076            11223 => 'Books|Young Adult|Coming of Age',
5077            11224 => 'Books|Young Adult|Social Studies',
5078            11225 => 'Books|Young Adult|Sports & Recreation',
5079            11226 => 'Books|Young Adult|Transportation',
5080            11227 => 'Books|Communications & Media',
5081            11228 => 'Books|Military & Warfare',
5082            11229 => 'Books|Romance|Inspirational',
5083            11231 => 'Books|Romance|Holiday',
5084            11232 => 'Books|Romance|Wholesome',
5085            11233 => 'Books|Romance|Military',
5086            11234 => 'Books|Arts & Entertainment|Art History',
5087            11236 => 'Books|Arts & Entertainment|Design',
5088            11243 => 'Books|Business & Personal Finance|Accounting',
5089            11244 => 'Books|Business & Personal Finance|Hospitality',
5090            11245 => 'Books|Business & Personal Finance|Real Estate',
5091            11246 => 'Books|Humor|Jokes & Riddles',
5092            11247 => 'Books|Religion & Spirituality|Comparative Religion',
5093            11255 => 'Books|Cookbooks, Food & Wine|Culinary Arts',
5094            11259 => 'Books|Mysteries & Thrillers|Cozy',
5095            11260 => 'Books|Politics & Current Events|Current Events',
5096            11261 => 'Books|Politics & Current Events|Foreign Policy & International Relations',
5097            11262 => 'Books|Politics & Current Events|Local Government',
5098            11263 => 'Books|Politics & Current Events|National Government',
5099            11264 => 'Books|Politics & Current Events|Political Science',
5100            11265 => 'Books|Politics & Current Events|Public Administration',
5101            11266 => 'Books|Politics & Current Events|World Affairs',
5102            11273 => 'Books|Nonfiction|Family & Relationships|Family & Childcare',
5103            11274 => 'Books|Nonfiction|Family & Relationships|Love & Romance',
5104            11275 => 'Books|Sci-Fi & Fantasy|Fantasy|Urban',
5105            11276 => 'Books|Reference|Foreign Languages|Arabic',
5106            11277 => 'Books|Reference|Foreign Languages|Bilingual Editions',
5107            11278 => 'Books|Reference|Foreign Languages|African Languages',
5108            11279 => 'Books|Reference|Foreign Languages|Ancient Languages',
5109            11280 => 'Books|Reference|Foreign Languages|Chinese',
5110            11281 => 'Books|Reference|Foreign Languages|English',
5111            11282 => 'Books|Reference|Foreign Languages|French',
5112            11283 => 'Books|Reference|Foreign Languages|German',
5113            11284 => 'Books|Reference|Foreign Languages|Hebrew',
5114            11285 => 'Books|Reference|Foreign Languages|Hindi',
5115            11286 => 'Books|Reference|Foreign Languages|Italian',
5116            11287 => 'Books|Reference|Foreign Languages|Japanese',
5117            11288 => 'Books|Reference|Foreign Languages|Korean',
5118            11289 => 'Books|Reference|Foreign Languages|Linguistics',
5119            11290 => 'Books|Reference|Foreign Languages|Other Languages',
5120            11291 => 'Books|Reference|Foreign Languages|Portuguese',
5121            11292 => 'Books|Reference|Foreign Languages|Russian',
5122            11293 => 'Books|Reference|Foreign Languages|Spanish',
5123            11294 => 'Books|Reference|Foreign Languages|Speech Pathology',
5124            11295 => 'Books|Science & Nature|Mathematics|Advanced Mathematics',
5125            11296 => 'Books|Science & Nature|Mathematics|Algebra',
5126            11297 => 'Books|Science & Nature|Mathematics|Arithmetic',
5127            11298 => 'Books|Science & Nature|Mathematics|Calculus',
5128            11299 => 'Books|Science & Nature|Mathematics|Geometry',
5129            11300 => 'Books|Science & Nature|Mathematics|Statistics',
5130            11301 => 'Books|Professional & Technical|Medical|Veterinary',
5131            11302 => 'Books|Professional & Technical|Medical|Neuroscience',
5132            11303 => 'Books|Professional & Technical|Medical|Immunology',
5133            11304 => 'Books|Professional & Technical|Medical|Nursing',
5134            11305 => 'Books|Professional & Technical|Medical|Pharmacology & Toxicology',
5135            11306 => 'Books|Professional & Technical|Medical|Anatomy & Physiology',
5136            11307 => 'Books|Professional & Technical|Medical|Dentistry',
5137            11308 => 'Books|Professional & Technical|Medical|Emergency Medicine',
5138            11309 => 'Books|Professional & Technical|Medical|Genetics',
5139            11310 => 'Books|Professional & Technical|Medical|Psychiatry',
5140            11311 => 'Books|Professional & Technical|Medical|Radiology',
5141            11312 => 'Books|Professional & Technical|Medical|Alternative Medicine',
5142            11317 => 'Books|Nonfiction|Philosophy|Political Philosophy',
5143            11319 => 'Books|Nonfiction|Philosophy|Philosophy of Language',
5144            11320 => 'Books|Nonfiction|Philosophy|Philosophy of Religion',
5145            11327 => 'Books|Nonfiction|Social Science|Sociology',
5146            11329 => 'Books|Professional & Technical|Engineering|Aeronautics',
5147            11330 => 'Books|Professional & Technical|Engineering|Chemical & Petroleum Engineering',
5148            11331 => 'Books|Professional & Technical|Engineering|Civil Engineering',
5149            11332 => 'Books|Professional & Technical|Engineering|Computer Science',
5150            11333 => 'Books|Professional & Technical|Engineering|Electrical Engineering',
5151            11334 => 'Books|Professional & Technical|Engineering|Environmental Engineering',
5152            11335 => 'Books|Professional & Technical|Engineering|Mechanical Engineering',
5153            11336 => 'Books|Professional & Technical|Engineering|Power Resources',
5154            11337 => 'Books|Comics & Graphic Novels|Manga|Boys',
5155            11338 => 'Books|Comics & Graphic Novels|Manga|Men',
5156            11339 => 'Books|Comics & Graphic Novels|Manga|Girls',
5157            11340 => 'Books|Comics & Graphic Novels|Manga|Women',
5158            11341 => 'Books|Comics & Graphic Novels|Manga|Other',
5159            11342 => 'Books|Comics & Graphic Novels|Manga|Yaoi',
5160            11343 => 'Books|Comics & Graphic Novels|Manga|Comic Essays',
5161            12001 => 'Mac App Store|Business',
5162            12002 => 'Mac App Store|Developer Tools',
5163            12003 => 'Mac App Store|Education',
5164            12004 => 'Mac App Store|Entertainment',
5165            12005 => 'Mac App Store|Finance',
5166            12006 => 'Mac App Store|Games',
5167            12007 => 'Mac App Store|Health & Fitness',
5168            12008 => 'Mac App Store|Lifestyle',
5169            12010 => 'Mac App Store|Medical',
5170            12011 => 'Mac App Store|Music',
5171            12012 => 'Mac App Store|News',
5172            12013 => 'Mac App Store|Photography',
5173            12014 => 'Mac App Store|Productivity',
5174            12015 => 'Mac App Store|Reference',
5175            12016 => 'Mac App Store|Social Networking',
5176            12017 => 'Mac App Store|Sports',
5177            12018 => 'Mac App Store|Travel',
5178            12019 => 'Mac App Store|Utilities',
5179            12020 => 'Mac App Store|Video',
5180            12021 => 'Mac App Store|Weather',
5181            12022 => 'Mac App Store|Graphics & Design',
5182            12201 => 'Mac App Store|Games|Action',
5183            12202 => 'Mac App Store|Games|Adventure',
5184            12203 => 'Mac App Store|Games|Casual',
5185            12204 => 'Mac App Store|Games|Board',
5186            12205 => 'Mac App Store|Games|Card',
5187            12206 => 'Mac App Store|Games|Casino',
5188            12207 => 'Mac App Store|Games|Dice',
5189            12208 => 'Mac App Store|Games|Educational',
5190            12209 => 'Mac App Store|Games|Family',
5191            12210 => 'Mac App Store|Games|Kids',
5192            12211 => 'Mac App Store|Games|Music',
5193            12212 => 'Mac App Store|Games|Puzzle',
5194            12213 => 'Mac App Store|Games|Racing',
5195            12214 => 'Mac App Store|Games|Role Playing',
5196            12215 => 'Mac App Store|Games|Simulation',
5197            12216 => 'Mac App Store|Games|Sports',
5198            12217 => 'Mac App Store|Games|Strategy',
5199            12218 => 'Mac App Store|Games|Trivia',
5200            12219 => 'Mac App Store|Games|Word',
5201            13001 => 'App Store|Magazines & Newspapers|News & Politics',
5202            13002 => 'App Store|Magazines & Newspapers|Fashion & Style',
5203            13003 => 'App Store|Magazines & Newspapers|Home & Garden',
5204            13004 => 'App Store|Magazines & Newspapers|Outdoors & Nature',
5205            13005 => 'App Store|Magazines & Newspapers|Sports & Leisure',
5206            13006 => 'App Store|Magazines & Newspapers|Automotive',
5207            13007 => 'App Store|Magazines & Newspapers|Arts & Photography',
5208            13008 => 'App Store|Magazines & Newspapers|Brides & Weddings',
5209            13009 => 'App Store|Magazines & Newspapers|Business & Investing',
5210            13010 => "App Store|Magazines & Newspapers|Children's Magazines",
5211            13011 => 'App Store|Magazines & Newspapers|Computers & Internet',
5212            13012 => 'App Store|Magazines & Newspapers|Cooking, Food & Drink',
5213            13013 => 'App Store|Magazines & Newspapers|Crafts & Hobbies',
5214            13014 => 'App Store|Magazines & Newspapers|Electronics & Audio',
5215            13015 => 'App Store|Magazines & Newspapers|Entertainment',
5216            13017 => 'App Store|Magazines & Newspapers|Health, Mind & Body',
5217            13018 => 'App Store|Magazines & Newspapers|History',
5218            13019 => 'App Store|Magazines & Newspapers|Literary Magazines & Journals',
5219            13020 => "App Store|Magazines & Newspapers|Men's Interest",
5220            13021 => 'App Store|Magazines & Newspapers|Movies & Music',
5221            13023 => 'App Store|Magazines & Newspapers|Parenting & Family',
5222            13024 => 'App Store|Magazines & Newspapers|Pets',
5223            13025 => 'App Store|Magazines & Newspapers|Professional & Trade',
5224            13026 => 'App Store|Magazines & Newspapers|Regional News',
5225            13027 => 'App Store|Magazines & Newspapers|Science',
5226            13028 => 'App Store|Magazines & Newspapers|Teens',
5227            13029 => 'App Store|Magazines & Newspapers|Travel & Regional',
5228            13030 => "App Store|Magazines & Newspapers|Women's Interest",
5229            15000 => 'Textbooks|Arts & Entertainment',
5230            15001 => 'Textbooks|Arts & Entertainment|Art & Architecture',
5231            15002 => 'Textbooks|Arts & Entertainment|Art & Architecture|Urban Planning',
5232            15003 => 'Textbooks|Arts & Entertainment|Art History',
5233            15004 => 'Textbooks|Arts & Entertainment|Dance',
5234            15005 => 'Textbooks|Arts & Entertainment|Design',
5235            15006 => 'Textbooks|Arts & Entertainment|Fashion',
5236            15007 => 'Textbooks|Arts & Entertainment|Film',
5237            15008 => 'Textbooks|Arts & Entertainment|Games',
5238            15009 => 'Textbooks|Arts & Entertainment|Interior Design',
5239            15010 => 'Textbooks|Arts & Entertainment|Media Arts',
5240            15011 => 'Textbooks|Arts & Entertainment|Music',
5241            15012 => 'Textbooks|Arts & Entertainment|Performing Arts',
5242            15013 => 'Textbooks|Arts & Entertainment|Photography',
5243            15014 => 'Textbooks|Arts & Entertainment|Theater',
5244            15015 => 'Textbooks|Arts & Entertainment|TV',
5245            15016 => 'Textbooks|Arts & Entertainment|Visual Arts',
5246            15017 => 'Textbooks|Biographies & Memoirs',
5247            15018 => 'Textbooks|Business & Personal Finance',
5248            15019 => 'Textbooks|Business & Personal Finance|Accounting',
5249            15020 => 'Textbooks|Business & Personal Finance|Careers',
5250            15021 => 'Textbooks|Business & Personal Finance|Economics',
5251            15022 => 'Textbooks|Business & Personal Finance|Finance',
5252            15023 => 'Textbooks|Business & Personal Finance|Hospitality',
5253            15024 => 'Textbooks|Business & Personal Finance|Industries & Professions',
5254            15025 => 'Textbooks|Business & Personal Finance|Investing',
5255            15026 => 'Textbooks|Business & Personal Finance|Management & Leadership',
5256            15027 => 'Textbooks|Business & Personal Finance|Marketing & Sales',
5257            15028 => 'Textbooks|Business & Personal Finance|Personal Finance',
5258            15029 => 'Textbooks|Business & Personal Finance|Real Estate',
5259            15030 => 'Textbooks|Business & Personal Finance|Reference',
5260            15031 => 'Textbooks|Business & Personal Finance|Small Business & Entrepreneurship',
5261            15032 => 'Textbooks|Children & Teens',
5262            15033 => 'Textbooks|Children & Teens|Fiction',
5263            15034 => 'Textbooks|Children & Teens|Nonfiction',
5264            15035 => 'Textbooks|Comics & Graphic Novels',
5265            15036 => 'Textbooks|Comics & Graphic Novels|Graphic Novels',
5266            15037 => 'Textbooks|Comics & Graphic Novels|Manga',
5267            15038 => 'Textbooks|Communications & Media',
5268            15039 => 'Textbooks|Communications & Media|Broadcasting',
5269            15040 => 'Textbooks|Communications & Media|Digital Media',
5270            15041 => 'Textbooks|Communications & Media|Journalism',
5271            15042 => 'Textbooks|Communications & Media|Photojournalism',
5272            15043 => 'Textbooks|Communications & Media|Print',
5273            15044 => 'Textbooks|Communications & Media|Speech',
5274            15045 => 'Textbooks|Communications & Media|Writing',
5275            15046 => 'Textbooks|Computers & Internet',
5276            15047 => 'Textbooks|Computers & Internet|Computers',
5277            15048 => 'Textbooks|Computers & Internet|Databases',
5278            15049 => 'Textbooks|Computers & Internet|Digital Media',
5279            15050 => 'Textbooks|Computers & Internet|Internet',
5280            15051 => 'Textbooks|Computers & Internet|Network',
5281            15052 => 'Textbooks|Computers & Internet|Operating Systems',
5282            15053 => 'Textbooks|Computers & Internet|Programming',
5283            15054 => 'Textbooks|Computers & Internet|Software',
5284            15055 => 'Textbooks|Computers & Internet|System Administration',
5285            15056 => 'Textbooks|Cookbooks, Food & Wine',
5286            15057 => 'Textbooks|Cookbooks, Food & Wine|Beverages',
5287            15058 => 'Textbooks|Cookbooks, Food & Wine|Courses & Dishes',
5288            15059 => 'Textbooks|Cookbooks, Food & Wine|Culinary Arts',
5289            15060 => 'Textbooks|Cookbooks, Food & Wine|Methods',
5290            15061 => 'Textbooks|Cookbooks, Food & Wine|Reference',
5291            15062 => 'Textbooks|Cookbooks, Food & Wine|Regional & Ethnic',
5292            15063 => 'Textbooks|Cookbooks, Food & Wine|Special Diet',
5293            15064 => 'Textbooks|Cookbooks, Food & Wine|Special Occasions',
5294            15065 => 'Textbooks|Cookbooks, Food & Wine|Specific Ingredients',
5295            15066 => 'Textbooks|Engineering',
5296            15067 => 'Textbooks|Engineering|Aeronautics',
5297            15068 => 'Textbooks|Engineering|Chemical & Petroleum Engineering',
5298            15069 => 'Textbooks|Engineering|Civil Engineering',
5299            15070 => 'Textbooks|Engineering|Computer Science',
5300            15071 => 'Textbooks|Engineering|Electrical Engineering',
5301            15072 => 'Textbooks|Engineering|Environmental Engineering',
5302            15073 => 'Textbooks|Engineering|Mechanical Engineering',
5303            15074 => 'Textbooks|Engineering|Power Resources',
5304            15075 => 'Textbooks|Fiction & Literature',
5305            15076 => 'Textbooks|Fiction & Literature|Latino',
5306            15077 => 'Textbooks|Fiction & Literature|Action & Adventure',
5307            15078 => 'Textbooks|Fiction & Literature|African American',
5308            15079 => 'Textbooks|Fiction & Literature|Anthologies',
5309            15080 => 'Textbooks|Fiction & Literature|Classics',
5310            15081 => 'Textbooks|Fiction & Literature|Comparative Literature',
5311            15082 => 'Textbooks|Fiction & Literature|Erotica',
5312            15083 => 'Textbooks|Fiction & Literature|Gay',
5313            15084 => 'Textbooks|Fiction & Literature|Ghost',
5314            15085 => 'Textbooks|Fiction & Literature|Historical',
5315            15086 => 'Textbooks|Fiction & Literature|Horror',
5316            15087 => 'Textbooks|Fiction & Literature|Literary',
5317            15088 => 'Textbooks|Fiction & Literature|Literary Criticism',
5318            15089 => 'Textbooks|Fiction & Literature|Poetry',
5319            15090 => 'Textbooks|Fiction & Literature|Religious',
5320            15091 => 'Textbooks|Fiction & Literature|Short Stories',
5321            15092 => 'Textbooks|Health, Mind & Body',
5322            15093 => 'Textbooks|Health, Mind & Body|Fitness',
5323            15094 => 'Textbooks|Health, Mind & Body|Self-Improvement',
5324            15095 => 'Textbooks|History',
5325            15096 => 'Textbooks|History|Africa',
5326            15097 => 'Textbooks|History|Americas',
5327            15098 => 'Textbooks|History|Americas|Canada',
5328            15099 => 'Textbooks|History|Americas|Latin America',
5329            15100 => 'Textbooks|History|Americas|United States',
5330            15101 => 'Textbooks|History|Ancient',
5331            15102 => 'Textbooks|History|Asia',
5332            15103 => 'Textbooks|History|Australia & Oceania',
5333            15104 => 'Textbooks|History|Europe',
5334            15105 => 'Textbooks|History|Middle East',
5335            15106 => 'Textbooks|History|Military',
5336            15107 => 'Textbooks|History|World',
5337            15108 => 'Textbooks|Humor',
5338            15109 => 'Textbooks|Language Studies',
5339            15110 => 'Textbooks|Language Studies|African Languages',
5340            15111 => 'Textbooks|Language Studies|Ancient Languages',
5341            15112 => 'Textbooks|Language Studies|Arabic',
5342            15113 => 'Textbooks|Language Studies|Bilingual Editions',
5343            15114 => 'Textbooks|Language Studies|Chinese',
5344            15115 => 'Textbooks|Language Studies|English',
5345            15116 => 'Textbooks|Language Studies|French',
5346            15117 => 'Textbooks|Language Studies|German',
5347            15118 => 'Textbooks|Language Studies|Hebrew',
5348            15119 => 'Textbooks|Language Studies|Hindi',
5349            15120 => 'Textbooks|Language Studies|Indigenous Languages',
5350            15121 => 'Textbooks|Language Studies|Italian',
5351            15122 => 'Textbooks|Language Studies|Japanese',
5352            15123 => 'Textbooks|Language Studies|Korean',
5353            15124 => 'Textbooks|Language Studies|Linguistics',
5354            15125 => 'Textbooks|Language Studies|Other Language',
5355            15126 => 'Textbooks|Language Studies|Portuguese',
5356            15127 => 'Textbooks|Language Studies|Russian',
5357            15128 => 'Textbooks|Language Studies|Spanish',
5358            15129 => 'Textbooks|Language Studies|Speech Pathology',
5359            15130 => 'Textbooks|Lifestyle & Home',
5360            15131 => 'Textbooks|Lifestyle & Home|Antiques & Collectibles',
5361            15132 => 'Textbooks|Lifestyle & Home|Crafts & Hobbies',
5362            15133 => 'Textbooks|Lifestyle & Home|Gardening',
5363            15134 => 'Textbooks|Lifestyle & Home|Pets',
5364            15135 => 'Textbooks|Mathematics',
5365            15136 => 'Textbooks|Mathematics|Advanced Mathematics',
5366            15137 => 'Textbooks|Mathematics|Algebra',
5367            15138 => 'Textbooks|Mathematics|Arithmetic',
5368            15139 => 'Textbooks|Mathematics|Calculus',
5369            15140 => 'Textbooks|Mathematics|Geometry',
5370            15141 => 'Textbooks|Mathematics|Statistics',
5371            15142 => 'Textbooks|Medicine',
5372            15143 => 'Textbooks|Medicine|Anatomy & Physiology',
5373            15144 => 'Textbooks|Medicine|Dentistry',
5374            15145 => 'Textbooks|Medicine|Emergency Medicine',
5375            15146 => 'Textbooks|Medicine|Genetics',
5376            15147 => 'Textbooks|Medicine|Immunology',
5377            15148 => 'Textbooks|Medicine|Neuroscience',
5378            15149 => 'Textbooks|Medicine|Nursing',
5379            15150 => 'Textbooks|Medicine|Pharmacology & Toxicology',
5380            15151 => 'Textbooks|Medicine|Psychiatry',
5381            15152 => 'Textbooks|Medicine|Psychology',
5382            15153 => 'Textbooks|Medicine|Radiology',
5383            15154 => 'Textbooks|Medicine|Veterinary',
5384            15155 => 'Textbooks|Mysteries & Thrillers',
5385            15156 => 'Textbooks|Mysteries & Thrillers|British Detectives',
5386            15157 => 'Textbooks|Mysteries & Thrillers|Hard-Boiled',
5387            15158 => 'Textbooks|Mysteries & Thrillers|Historical',
5388            15159 => 'Textbooks|Mysteries & Thrillers|Police Procedural',
5389            15160 => 'Textbooks|Mysteries & Thrillers|Short Stories',
5390            15161 => 'Textbooks|Mysteries & Thrillers|Women Sleuths',
5391            15162 => 'Textbooks|Nonfiction',
5392            15163 => 'Textbooks|Nonfiction|Family & Relationships',
5393            15164 => 'Textbooks|Nonfiction|Transportation',
5394            15165 => 'Textbooks|Nonfiction|True Crime',
5395            15166 => 'Textbooks|Parenting',
5396            15167 => 'Textbooks|Philosophy',
5397            15168 => 'Textbooks|Philosophy|Aesthetics',
5398            15169 => 'Textbooks|Philosophy|Epistemology',
5399            15170 => 'Textbooks|Philosophy|Ethics',
5400            15171 => 'Textbooks|Philosophy|Philosophy of Language',
5401            15172 => 'Textbooks|Philosophy|Logic',
5402            15173 => 'Textbooks|Philosophy|Metaphysics',
5403            15174 => 'Textbooks|Philosophy|Political Philosophy',
5404            15175 => 'Textbooks|Philosophy|Philosophy of Religion',
5405            15176 => 'Textbooks|Politics & Current Events',
5406            15177 => 'Textbooks|Politics & Current Events|Current Events',
5407            15178 => 'Textbooks|Politics & Current Events|Foreign Policy & International Relations',
5408            15179 => 'Textbooks|Politics & Current Events|Local Governments',
5409            15180 => 'Textbooks|Politics & Current Events|National Governments',
5410            15181 => 'Textbooks|Politics & Current Events|Political Science',
5411            15182 => 'Textbooks|Politics & Current Events|Public Administration',
5412            15183 => 'Textbooks|Politics & Current Events|World Affairs',
5413            15184 => 'Textbooks|Professional & Technical',
5414            15185 => 'Textbooks|Professional & Technical|Design',
5415            15186 => 'Textbooks|Professional & Technical|Language Arts & Disciplines',
5416            15187 => 'Textbooks|Professional & Technical|Engineering',
5417            15188 => 'Textbooks|Professional & Technical|Law',
5418            15189 => 'Textbooks|Professional & Technical|Medical',
5419            15190 => 'Textbooks|Reference',
5420            15191 => 'Textbooks|Reference|Almanacs & Yearbooks',
5421            15192 => 'Textbooks|Reference|Atlases & Maps',
5422            15193 => 'Textbooks|Reference|Catalogs & Directories',
5423            15194 => 'Textbooks|Reference|Consumer Guides',
5424            15195 => 'Textbooks|Reference|Dictionaries & Thesauruses',
5425            15196 => 'Textbooks|Reference|Encyclopedias',
5426            15197 => 'Textbooks|Reference|Etiquette',
5427            15198 => 'Textbooks|Reference|Quotations',
5428            15199 => 'Textbooks|Reference|Study Aids',
5429            15200 => 'Textbooks|Reference|Words & Language',
5430            15201 => 'Textbooks|Reference|Writing',
5431            15202 => 'Textbooks|Religion & Spirituality',
5432            15203 => 'Textbooks|Religion & Spirituality|Bible Studies',
5433            15204 => 'Textbooks|Religion & Spirituality|Bibles',
5434            15205 => 'Textbooks|Religion & Spirituality|Buddhism',
5435            15206 => 'Textbooks|Religion & Spirituality|Christianity',
5436            15207 => 'Textbooks|Religion & Spirituality|Comparative Religion',
5437            15208 => 'Textbooks|Religion & Spirituality|Hinduism',
5438            15209 => 'Textbooks|Religion & Spirituality|Islam',
5439            15210 => 'Textbooks|Religion & Spirituality|Judaism',
5440            15211 => 'Textbooks|Religion & Spirituality|Spirituality',
5441            15212 => 'Textbooks|Romance',
5442            15213 => 'Textbooks|Romance|Contemporary',
5443            15214 => 'Textbooks|Romance|Erotic Romance',
5444            15215 => 'Textbooks|Romance|Paranormal',
5445            15216 => 'Textbooks|Romance|Historical',
5446            15217 => 'Textbooks|Romance|Short Stories',
5447            15218 => 'Textbooks|Romance|Suspense',
5448            15219 => 'Textbooks|Romance|Western',
5449            15220 => 'Textbooks|Sci-Fi & Fantasy',
5450            15221 => 'Textbooks|Sci-Fi & Fantasy|Fantasy',
5451            15222 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Contemporary',
5452            15223 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Epic',
5453            15224 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Historical',
5454            15225 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Paranormal',
5455            15226 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Short Stories',
5456            15227 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction',
5457            15228 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature',
5458            15229 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature|Adventure',
5459            15230 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature|High Tech',
5460            15231 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature|Short Stories',
5461            15232 => 'Textbooks|Science & Nature',
5462            15233 => 'Textbooks|Science & Nature|Agriculture',
5463            15234 => 'Textbooks|Science & Nature|Astronomy',
5464            15235 => 'Textbooks|Science & Nature|Atmosphere',
5465            15236 => 'Textbooks|Science & Nature|Biology',
5466            15237 => 'Textbooks|Science & Nature|Chemistry',
5467            15238 => 'Textbooks|Science & Nature|Earth Sciences',
5468            15239 => 'Textbooks|Science & Nature|Ecology',
5469            15240 => 'Textbooks|Science & Nature|Environment',
5470            15241 => 'Textbooks|Science & Nature|Essays',
5471            15242 => 'Textbooks|Science & Nature|Geography',
5472            15243 => 'Textbooks|Science & Nature|Geology',
5473            15244 => 'Textbooks|Science & Nature|History',
5474            15245 => 'Textbooks|Science & Nature|Life Sciences',
5475            15246 => 'Textbooks|Science & Nature|Nature',
5476            15247 => 'Textbooks|Science & Nature|Physics',
5477            15248 => 'Textbooks|Science & Nature|Reference',
5478            15249 => 'Textbooks|Social Science',
5479            15250 => 'Textbooks|Social Science|Anthropology',
5480            15251 => 'Textbooks|Social Science|Archaeology',
5481            15252 => 'Textbooks|Social Science|Civics',
5482            15253 => 'Textbooks|Social Science|Government',
5483            15254 => 'Textbooks|Social Science|Social Studies',
5484            15255 => 'Textbooks|Social Science|Social Welfare',
5485            15256 => 'Textbooks|Social Science|Society',
5486            15257 => 'Textbooks|Social Science|Society|African Studies',
5487            15258 => 'Textbooks|Social Science|Society|American Studies',
5488            15259 => 'Textbooks|Social Science|Society|Asia Pacific Studies',
5489            15260 => 'Textbooks|Social Science|Society|Cross-Cultural Studies',
5490            15261 => 'Textbooks|Social Science|Society|European Studies',
5491            15262 => 'Textbooks|Social Science|Society|Immigration & Emigration',
5492            15263 => 'Textbooks|Social Science|Society|Indigenous Studies',
5493            15264 => 'Textbooks|Social Science|Society|Latin & Caribbean Studies',
5494            15265 => 'Textbooks|Social Science|Society|Middle Eastern Studies',
5495            15266 => 'Textbooks|Social Science|Society|Race & Ethnicity Studies',
5496            15267 => 'Textbooks|Social Science|Society|Sexuality Studies',
5497            15268 => "Textbooks|Social Science|Society|Women's Studies",
5498            15269 => 'Textbooks|Social Science|Sociology',
5499            15270 => 'Textbooks|Sports & Outdoors',
5500            15271 => 'Textbooks|Sports & Outdoors|Baseball',
5501            15272 => 'Textbooks|Sports & Outdoors|Basketball',
5502            15273 => 'Textbooks|Sports & Outdoors|Coaching',
5503            15274 => 'Textbooks|Sports & Outdoors|Equestrian',
5504            15275 => 'Textbooks|Sports & Outdoors|Extreme Sports',
5505            15276 => 'Textbooks|Sports & Outdoors|Football',
5506            15277 => 'Textbooks|Sports & Outdoors|Golf',
5507            15278 => 'Textbooks|Sports & Outdoors|Hockey',
5508            15279 => 'Textbooks|Sports & Outdoors|Motor Sports',
5509            15280 => 'Textbooks|Sports & Outdoors|Mountaineering',
5510            15281 => 'Textbooks|Sports & Outdoors|Outdoors',
5511            15282 => 'Textbooks|Sports & Outdoors|Racket Sports',
5512            15283 => 'Textbooks|Sports & Outdoors|Reference',
5513            15284 => 'Textbooks|Sports & Outdoors|Soccer',
5514            15285 => 'Textbooks|Sports & Outdoors|Training',
5515            15286 => 'Textbooks|Sports & Outdoors|Water Sports',
5516            15287 => 'Textbooks|Sports & Outdoors|Winter Sports',
5517            15288 => 'Textbooks|Teaching & Learning',
5518            15289 => 'Textbooks|Teaching & Learning|Adult Education',
5519            15290 => 'Textbooks|Teaching & Learning|Curriculum & Teaching',
5520            15291 => 'Textbooks|Teaching & Learning|Educational Leadership',
5521            15292 => 'Textbooks|Teaching & Learning|Educational Technology',
5522            15293 => 'Textbooks|Teaching & Learning|Family & Childcare',
5523            15294 => 'Textbooks|Teaching & Learning|Information & Library Science',
5524            15295 => 'Textbooks|Teaching & Learning|Learning Resources',
5525            15296 => 'Textbooks|Teaching & Learning|Psychology & Research',
5526            15297 => 'Textbooks|Teaching & Learning|Special Education',
5527            15298 => 'Textbooks|Travel & Adventure',
5528            15299 => 'Textbooks|Travel & Adventure|Africa',
5529            15300 => 'Textbooks|Travel & Adventure|Americas',
5530            15301 => 'Textbooks|Travel & Adventure|Americas|Canada',
5531            15302 => 'Textbooks|Travel & Adventure|Americas|Latin America',
5532            15303 => 'Textbooks|Travel & Adventure|Americas|United States',
5533            15304 => 'Textbooks|Travel & Adventure|Asia',
5534            15305 => 'Textbooks|Travel & Adventure|Caribbean',
5535            15306 => 'Textbooks|Travel & Adventure|Essays & Memoirs',
5536            15307 => 'Textbooks|Travel & Adventure|Europe',
5537            15308 => 'Textbooks|Travel & Adventure|Middle East',
5538            15309 => 'Textbooks|Travel & Adventure|Oceania',
5539            15310 => 'Textbooks|Travel & Adventure|Specialty Travel',
5540            15311 => 'Textbooks|Comics & Graphic Novels|Comics',
5541            15312 => 'Textbooks|Reference|Manuals',
5542            16001 => 'App Store|Stickers|Emoji & Expressions',
5543            16003 => 'App Store|Stickers|Animals & Nature',
5544            16005 => 'App Store|Stickers|Art',
5545            16006 => 'App Store|Stickers|Celebrations',
5546            16007 => 'App Store|Stickers|Celebrities',
5547            16008 => 'App Store|Stickers|Comics & Cartoons',
5548            16009 => 'App Store|Stickers|Eating & Drinking',
5549            16010 => 'App Store|Stickers|Gaming',
5550            16014 => 'App Store|Stickers|Movies & TV',
5551            16015 => 'App Store|Stickers|Music',
5552            16017 => 'App Store|Stickers|People',
5553            16019 => 'App Store|Stickers|Places & Objects',
5554            16021 => 'App Store|Stickers|Sports & Activities',
5555            16025 => 'App Store|Stickers|Kids & Family',
5556            16026 => 'App Store|Stickers|Fashion',
5557            100000 => 'Music|Christian & Gospel',
5558            100001 => 'Music|Classical|Art Song',
5559            100002 => 'Music|Classical|Brass & Woodwinds',
5560            100003 => 'Music|Classical|Solo Instrumental',
5561            100004 => 'Music|Classical|Contemporary Era',
5562            100005 => 'Music|Classical|Oratorio',
5563            100006 => 'Music|Classical|Cantata',
5564            100007 => 'Music|Classical|Electronic',
5565            100008 => 'Music|Classical|Sacred',
5566            100009 => 'Music|Classical|Guitar',
5567            100010 => 'Music|Classical|Piano',
5568            100011 => 'Music|Classical|Violin',
5569            100012 => 'Music|Classical|Cello',
5570            100013 => 'Music|Classical|Percussion',
5571            100014 => 'Music|Electronic|Dubstep',
5572            100015 => 'Music|Electronic|Bass',
5573            100016 => 'Music|Hip-Hop/Rap|UK Hip-Hop',
5574            100017 => 'Music|Reggae|Lovers Rock',
5575            100018 => 'Music|Alternative|EMO',
5576            100019 => 'Music|Alternative|Pop Punk',
5577            100020 => 'Music|Alternative|Indie Pop',
5578            100021 => 'Music|New Age|Yoga',
5579            100022 => 'Music|Pop|Tribute',
5580            100023 => 'Music|Pop|Shows',
5581            100024 => 'Music|Cuban',
5582            100025 => 'Music|Cuban|Mambo',
5583            100026 => 'Music|Cuban|Chachacha',
5584            100027 => 'Music|Cuban|Guajira',
5585            100028 => 'Music|Cuban|Son',
5586            100029 => 'Music|Cuban|Bolero',
5587            100030 => 'Music|Cuban|Guaracha',
5588            100031 => 'Music|Cuban|Timba',
5589            100032 => 'Music|Soundtrack|Video Game',
5590            100033 => 'Music|Indian|Regional Indian|Punjabi|Punjabi Pop',
5591            100034 => 'Music|Indian|Regional Indian|Bengali|Rabindra Sangeet',
5592            100035 => 'Music|Indian|Regional Indian|Malayalam',
5593            100036 => 'Music|Indian|Regional Indian|Kannada',
5594            100037 => 'Music|Indian|Regional Indian|Marathi',
5595            100038 => 'Music|Indian|Regional Indian|Gujarati',
5596            100039 => 'Music|Indian|Regional Indian|Assamese',
5597            100040 => 'Music|Indian|Regional Indian|Bhojpuri',
5598            100041 => 'Music|Indian|Regional Indian|Haryanvi',
5599            100042 => 'Music|Indian|Regional Indian|Odia',
5600            100043 => 'Music|Indian|Regional Indian|Rajasthani',
5601            100044 => 'Music|Indian|Regional Indian|Urdu',
5602            100045 => 'Music|Indian|Regional Indian|Punjabi',
5603            100046 => 'Music|Indian|Regional Indian|Bengali',
5604            100047 => 'Music|Indian|Indian Classical|Carnatic Classical',
5605            100048 => 'Music|Indian|Indian Classical|Hindustani Classical',
5606            100049 => 'Music|African|Afro House',
5607            100050 => 'Music|African|Afro Soul',
5608            100051 => 'Music|African|Afrobeats',
5609            100052 => 'Music|African|Benga',
5610            100053 => 'Music|African|Bongo-Flava',
5611            100054 => 'Music|African|Coupe-Decale',
5612            100055 => 'Music|African|Gqom',
5613            100056 => 'Music|African|Highlife',
5614            100057 => 'Music|African|Kuduro',
5615            100058 => 'Music|African|Kizomba',
5616            100059 => 'Music|African|Kwaito',
5617            100060 => 'Music|African|Mbalax',
5618            100061 => 'Music|African|Ndombolo',
5619            100062 => 'Music|African|Shangaan Electro',
5620            100063 => 'Music|African|Soukous',
5621            100064 => 'Music|African|Taarab',
5622            100065 => 'Music|African|Zouglou',
5623            100066 => 'Music|Turkish|Ozgun',
5624            100067 => 'Music|Turkish|Fantezi',
5625            100068 => 'Music|Turkish|Religious',
5626            100069 => 'Music|Pop|Turkish Pop',
5627            100070 => 'Music|Rock|Turkish Rock',
5628            100071 => 'Music|Alternative|Turkish Alternative',
5629            100072 => 'Music|Hip-Hop/Rap|Turkish Hip-Hop/Rap',
5630            100073 => 'Music|African|Maskandi',
5631            100074 => 'Music|Russian|Russian Romance',
5632            100075 => 'Music|Russian|Russian Bard',
5633            100076 => 'Music|Russian|Russian Pop',
5634            100077 => 'Music|Russian|Russian Rock',
5635            100078 => 'Music|Russian|Russian Hip-Hop',
5636            100079 => 'Music|Arabic|Levant',
5637            100080 => 'Music|Arabic|Levant|Dabke',
5638            100081 => 'Music|Arabic|Maghreb Rai',
5639            100082 => 'Music|Arabic|Khaleeji|Khaleeji Jalsat',
5640            100083 => 'Music|Arabic|Khaleeji|Khaleeji Shailat',
5641            100084 => 'Music|Tarab',
5642            100085 => 'Music|Tarab|Iraqi Tarab',
5643            100086 => 'Music|Tarab|Egyptian Tarab',
5644            100087 => 'Music|Tarab|Khaleeji Tarab',
5645            100088 => 'Music|Pop|Levant Pop',
5646            100089 => 'Music|Pop|Iraqi Pop',
5647            100090 => 'Music|Pop|Egyptian Pop',
5648            100091 => 'Music|Pop|Maghreb Pop',
5649            100092 => 'Music|Pop|Khaleeji Pop',
5650            100093 => 'Music|Hip-Hop/Rap|Levant Hip-Hop',
5651            100094 => 'Music|Hip-Hop/Rap|Egyptian Hip-Hop',
5652            100095 => 'Music|Hip-Hop/Rap|Maghreb Hip-Hop',
5653            100096 => 'Music|Hip-Hop/Rap|Khaleeji Hip-Hop',
5654            100097 => 'Music|Alternative|Indie Levant',
5655            100098 => 'Music|Alternative|Indie Egyptian',
5656            100099 => 'Music|Alternative|Indie Maghreb',
5657            100100 => 'Music|Electronic|Levant Electronic',
5658            100101 => "Music|Electronic|Electro-Cha'abi",
5659            100102 => 'Music|Electronic|Maghreb Electronic',
5660            100103 => 'Music|Folk|Iraqi Folk',
5661            100104 => 'Music|Folk|Khaleeji Folk',
5662            100105 => 'Music|Dance|Maghreb Dance',
5663            40000000 => 'iTunes U',
5664            40000001 => 'iTunes U|Business & Economics',
5665            40000002 => 'iTunes U|Business & Economics|Economics',
5666            40000003 => 'iTunes U|Business & Economics|Finance',
5667            40000004 => 'iTunes U|Business & Economics|Hospitality',
5668            40000005 => 'iTunes U|Business & Economics|Management',
5669            40000006 => 'iTunes U|Business & Economics|Marketing',
5670            40000007 => 'iTunes U|Business & Economics|Personal Finance',
5671            40000008 => 'iTunes U|Business & Economics|Real Estate',
5672            40000009 => 'iTunes U|Engineering',
5673            40000010 => 'iTunes U|Engineering|Chemical & Petroleum Engineering',
5674            40000011 => 'iTunes U|Engineering|Civil Engineering',
5675            40000012 => 'iTunes U|Engineering|Computer Science',
5676            40000013 => 'iTunes U|Engineering|Electrical Engineering',
5677            40000014 => 'iTunes U|Engineering|Environmental Engineering',
5678            40000015 => 'iTunes U|Engineering|Mechanical Engineering',
5679            40000016 => 'iTunes U|Music, Art, & Design',
5680            40000017 => 'iTunes U|Music, Art, & Design|Architecture',
5681            40000019 => 'iTunes U|Music, Art, & Design|Art History',
5682            40000020 => 'iTunes U|Music, Art, & Design|Dance',
5683            40000021 => 'iTunes U|Music, Art, & Design|Film',
5684            40000022 => 'iTunes U|Music, Art, & Design|Design',
5685            40000023 => 'iTunes U|Music, Art, & Design|Interior Design',
5686            40000024 => 'iTunes U|Music, Art, & Design|Music',
5687            40000025 => 'iTunes U|Music, Art, & Design|Theater',
5688            40000026 => 'iTunes U|Health & Medicine',
5689            40000027 => 'iTunes U|Health & Medicine|Anatomy & Physiology',
5690            40000028 => 'iTunes U|Health & Medicine|Behavioral Science',
5691            40000029 => 'iTunes U|Health & Medicine|Dentistry',
5692            40000030 => 'iTunes U|Health & Medicine|Diet & Nutrition',
5693            40000031 => 'iTunes U|Health & Medicine|Emergency Medicine',
5694            40000032 => 'iTunes U|Health & Medicine|Genetics',
5695            40000033 => 'iTunes U|Health & Medicine|Gerontology',
5696            40000034 => 'iTunes U|Health & Medicine|Health & Exercise Science',
5697            40000035 => 'iTunes U|Health & Medicine|Immunology',
5698            40000036 => 'iTunes U|Health & Medicine|Neuroscience',
5699            40000037 => 'iTunes U|Health & Medicine|Pharmacology & Toxicology',
5700            40000038 => 'iTunes U|Health & Medicine|Psychiatry',
5701            40000039 => 'iTunes U|Health & Medicine|Global Health',
5702            40000040 => 'iTunes U|Health & Medicine|Radiology',
5703            40000041 => 'iTunes U|History',
5704            40000042 => 'iTunes U|History|Ancient History',
5705            40000043 => 'iTunes U|History|Medieval History',
5706            40000044 => 'iTunes U|History|Military History',
5707            40000045 => 'iTunes U|History|Modern History',
5708            40000046 => 'iTunes U|History|African History',
5709            40000047 => 'iTunes U|History|Asia-Pacific History',
5710            40000048 => 'iTunes U|History|European History',
5711            40000049 => 'iTunes U|History|Middle Eastern History',
5712            40000050 => 'iTunes U|History|North American History',
5713            40000051 => 'iTunes U|History|South American History',
5714            40000053 => 'iTunes U|Communications & Journalism',
5715            40000054 => 'iTunes U|Philosophy',
5716            40000055 => 'iTunes U|Religion & Spirituality',
5717            40000056 => 'iTunes U|Languages',
5718            40000057 => 'iTunes U|Languages|African Languages',
5719            40000058 => 'iTunes U|Languages|Ancient Languages',
5720            40000061 => 'iTunes U|Languages|English',
5721            40000063 => 'iTunes U|Languages|French',
5722            40000064 => 'iTunes U|Languages|German',
5723            40000065 => 'iTunes U|Languages|Italian',
5724            40000066 => 'iTunes U|Languages|Linguistics',
5725            40000068 => 'iTunes U|Languages|Spanish',
5726            40000069 => 'iTunes U|Languages|Speech Pathology',
5727            40000070 => 'iTunes U|Writing & Literature',
5728            40000071 => 'iTunes U|Writing & Literature|Anthologies',
5729            40000072 => 'iTunes U|Writing & Literature|Biography',
5730            40000073 => 'iTunes U|Writing & Literature|Classics',
5731            40000074 => 'iTunes U|Writing & Literature|Literary Criticism',
5732            40000075 => 'iTunes U|Writing & Literature|Fiction',
5733            40000076 => 'iTunes U|Writing & Literature|Poetry',
5734            40000077 => 'iTunes U|Mathematics',
5735            40000078 => 'iTunes U|Mathematics|Advanced Mathematics',
5736            40000079 => 'iTunes U|Mathematics|Algebra',
5737            40000080 => 'iTunes U|Mathematics|Arithmetic',
5738            40000081 => 'iTunes U|Mathematics|Calculus',
5739            40000082 => 'iTunes U|Mathematics|Geometry',
5740            40000083 => 'iTunes U|Mathematics|Statistics',
5741            40000084 => 'iTunes U|Science',
5742            40000085 => 'iTunes U|Science|Agricultural',
5743            40000086 => 'iTunes U|Science|Astronomy',
5744            40000087 => 'iTunes U|Science|Atmosphere',
5745            40000088 => 'iTunes U|Science|Biology',
5746            40000089 => 'iTunes U|Science|Chemistry',
5747            40000090 => 'iTunes U|Science|Ecology',
5748            40000091 => 'iTunes U|Science|Geography',
5749            40000092 => 'iTunes U|Science|Geology',
5750            40000093 => 'iTunes U|Science|Physics',
5751            40000094 => 'iTunes U|Social Science',
5752            40000095 => 'iTunes U|Law & Politics|Law',
5753            40000096 => 'iTunes U|Law & Politics|Political Science',
5754            40000097 => 'iTunes U|Law & Politics|Public Administration',
5755            40000098 => 'iTunes U|Social Science|Psychology',
5756            40000099 => 'iTunes U|Social Science|Social Welfare',
5757            40000100 => 'iTunes U|Social Science|Sociology',
5758            40000101 => 'iTunes U|Society',
5759            40000103 => 'iTunes U|Society|Asia Pacific Studies',
5760            40000104 => 'iTunes U|Society|European Studies',
5761            40000105 => 'iTunes U|Society|Indigenous Studies',
5762            40000106 => 'iTunes U|Society|Latin & Caribbean Studies',
5763            40000107 => 'iTunes U|Society|Middle Eastern Studies',
5764            40000108 => "iTunes U|Society|Women's Studies",
5765            40000109 => 'iTunes U|Teaching & Learning',
5766            40000110 => 'iTunes U|Teaching & Learning|Curriculum & Teaching',
5767            40000111 => 'iTunes U|Teaching & Learning|Educational Leadership',
5768            40000112 => 'iTunes U|Teaching & Learning|Family & Childcare',
5769            40000113 => 'iTunes U|Teaching & Learning|Learning Resources',
5770            40000114 => 'iTunes U|Teaching & Learning|Psychology & Research',
5771            40000115 => 'iTunes U|Teaching & Learning|Special Education',
5772            40000116 => 'iTunes U|Music, Art, & Design|Culinary Arts',
5773            40000117 => 'iTunes U|Music, Art, & Design|Fashion',
5774            40000118 => 'iTunes U|Music, Art, & Design|Media Arts',
5775            40000119 => 'iTunes U|Music, Art, & Design|Photography',
5776            40000120 => 'iTunes U|Music, Art, & Design|Visual Art',
5777            40000121 => 'iTunes U|Business & Economics|Entrepreneurship',
5778            40000122 => 'iTunes U|Communications & Journalism|Broadcasting',
5779            40000123 => 'iTunes U|Communications & Journalism|Digital Media',
5780            40000124 => 'iTunes U|Communications & Journalism|Journalism',
5781            40000125 => 'iTunes U|Communications & Journalism|Photojournalism',
5782            40000126 => 'iTunes U|Communications & Journalism|Print',
5783            40000127 => 'iTunes U|Communications & Journalism|Speech',
5784            40000128 => 'iTunes U|Communications & Journalism|Writing',
5785            40000129 => 'iTunes U|Health & Medicine|Nursing',
5786            40000130 => 'iTunes U|Languages|Arabic',
5787            40000131 => 'iTunes U|Languages|Chinese',
5788            40000132 => 'iTunes U|Languages|Hebrew',
5789            40000133 => 'iTunes U|Languages|Hindi',
5790            40000134 => 'iTunes U|Languages|Indigenous Languages',
5791            40000135 => 'iTunes U|Languages|Japanese',
5792            40000136 => 'iTunes U|Languages|Korean',
5793            40000137 => 'iTunes U|Languages|Other Languages',
5794            40000138 => 'iTunes U|Languages|Portuguese',
5795            40000139 => 'iTunes U|Languages|Russian',
5796            40000140 => 'iTunes U|Law & Politics',
5797            40000141 => 'iTunes U|Law & Politics|Foreign Policy & International Relations',
5798            40000142 => 'iTunes U|Law & Politics|Local Governments',
5799            40000143 => 'iTunes U|Law & Politics|National Governments',
5800            40000144 => 'iTunes U|Law & Politics|World Affairs',
5801            40000145 => 'iTunes U|Writing & Literature|Comparative Literature',
5802            40000146 => 'iTunes U|Philosophy|Aesthetics',
5803            40000147 => 'iTunes U|Philosophy|Epistemology',
5804            40000148 => 'iTunes U|Philosophy|Ethics',
5805            40000149 => 'iTunes U|Philosophy|Metaphysics',
5806            40000150 => 'iTunes U|Philosophy|Political Philosophy',
5807            40000151 => 'iTunes U|Philosophy|Logic',
5808            40000152 => 'iTunes U|Philosophy|Philosophy of Language',
5809            40000153 => 'iTunes U|Philosophy|Philosophy of Religion',
5810            40000154 => 'iTunes U|Social Science|Archaeology',
5811            40000155 => 'iTunes U|Social Science|Anthropology',
5812            40000156 => 'iTunes U|Religion & Spirituality|Buddhism',
5813            40000157 => 'iTunes U|Religion & Spirituality|Christianity',
5814            40000158 => 'iTunes U|Religion & Spirituality|Comparative Religion',
5815            40000159 => 'iTunes U|Religion & Spirituality|Hinduism',
5816            40000160 => 'iTunes U|Religion & Spirituality|Islam',
5817            40000161 => 'iTunes U|Religion & Spirituality|Judaism',
5818            40000162 => 'iTunes U|Religion & Spirituality|Other Religions',
5819            40000163 => 'iTunes U|Religion & Spirituality|Spirituality',
5820            40000164 => 'iTunes U|Science|Environment',
5821            40000165 => 'iTunes U|Society|African Studies',
5822            40000166 => 'iTunes U|Society|American Studies',
5823            40000167 => 'iTunes U|Society|Cross-cultural Studies',
5824            40000168 => 'iTunes U|Society|Immigration & Emigration',
5825            40000169 => 'iTunes U|Society|Race & Ethnicity Studies',
5826            40000170 => 'iTunes U|Society|Sexuality Studies',
5827            40000171 => 'iTunes U|Teaching & Learning|Educational Technology',
5828            40000172 => 'iTunes U|Teaching & Learning|Information/Library Science',
5829            40000173 => 'iTunes U|Languages|Dutch',
5830            40000174 => 'iTunes U|Languages|Luxembourgish',
5831            40000175 => 'iTunes U|Languages|Swedish',
5832            40000176 => 'iTunes U|Languages|Norwegian',
5833            40000177 => 'iTunes U|Languages|Finnish',
5834            40000178 => 'iTunes U|Languages|Danish',
5835            40000179 => 'iTunes U|Languages|Polish',
5836            40000180 => 'iTunes U|Languages|Turkish',
5837            40000181 => 'iTunes U|Languages|Flemish',
5838            50000024 => 'Audiobooks',
5839            50000040 => 'Audiobooks|Fiction',
5840            50000041 => 'Audiobooks|Arts & Entertainment',
5841            50000042 => 'Audiobooks|Biographies & Memoirs',
5842            50000043 => 'Audiobooks|Business & Personal Finance',
5843            50000044 => 'Audiobooks|Kids & Young Adults',
5844            50000045 => 'Audiobooks|Classics',
5845            50000046 => 'Audiobooks|Comedy',
5846            50000047 => 'Audiobooks|Drama & Poetry',
5847            50000048 => 'Audiobooks|Speakers & Storytellers',
5848            50000049 => 'Audiobooks|History',
5849            50000050 => 'Audiobooks|Languages',
5850            50000051 => 'Audiobooks|Mysteries & Thrillers',
5851            50000052 => 'Audiobooks|Nonfiction',
5852            50000053 => 'Audiobooks|Religion & Spirituality',
5853            50000054 => 'Audiobooks|Science & Nature',
5854            50000055 => 'Audiobooks|Sci Fi & Fantasy',
5855            50000056 => 'Audiobooks|Self-Development',
5856            50000057 => 'Audiobooks|Sports & Outdoors',
5857            50000058 => 'Audiobooks|Technology',
5858            50000059 => 'Audiobooks|Travel & Adventure',
5859            50000061 => 'Music|Spoken Word',
5860            50000063 => 'Music|Disney',
5861            50000064 => 'Music|French Pop',
5862            50000066 => 'Music|German Pop',
5863            50000068 => 'Music|German Folk',
5864            50000069 => 'Audiobooks|Romance',
5865            50000070 => 'Audiobooks|Audiobooks Latino',
5866            50000071 => 'Books|Comics & Graphic Novels|Manga|Action',
5867            50000072 => 'Books|Comics & Graphic Novels|Manga|Comedy',
5868            50000073 => 'Books|Comics & Graphic Novels|Manga|Erotica',
5869            50000074 => 'Books|Comics & Graphic Novels|Manga|Fantasy',
5870            50000075 => 'Books|Comics & Graphic Novels|Manga|Four Cell Manga',
5871            50000076 => 'Books|Comics & Graphic Novels|Manga|Gay & Lesbian',
5872            50000077 => 'Books|Comics & Graphic Novels|Manga|Hard-Boiled',
5873            50000078 => 'Books|Comics & Graphic Novels|Manga|Heroes',
5874            50000079 => 'Books|Comics & Graphic Novels|Manga|Historical Fiction',
5875            50000080 => 'Books|Comics & Graphic Novels|Manga|Mecha',
5876            50000081 => 'Books|Comics & Graphic Novels|Manga|Mystery',
5877            50000082 => 'Books|Comics & Graphic Novels|Manga|Nonfiction',
5878            50000083 => 'Books|Comics & Graphic Novels|Manga|Religious',
5879            50000084 => 'Books|Comics & Graphic Novels|Manga|Romance',
5880            50000085 => 'Books|Comics & Graphic Novels|Manga|Romantic Comedy',
5881            50000086 => 'Books|Comics & Graphic Novels|Manga|Science Fiction',
5882            50000087 => 'Books|Comics & Graphic Novels|Manga|Sports',
5883            50000088 => 'Books|Fiction & Literature|Light Novels',
5884            50000089 => 'Books|Comics & Graphic Novels|Manga|Horror',
5885            50000090 => 'Books|Comics & Graphic Novels|Comics',
5886            50000091 => 'Books|Romance|Multicultural',
5887            50000092 => 'Audiobooks|Erotica',
5888            50000093 => 'Audiobooks|Light Novels',
5889        },
5890    },
5891    grup => { Name => 'Grouping', Avoid => 1 }, #10
5892    hdvd => { #10
5893        Name => 'HDVideo',
5894        Format => 'int8u', #24
5895        Writable => 'int8s', #27
5896        PrintConv => { 0 => 'No', 1 => 'Yes' },
5897    },
5898    keyw => 'Keyword', #7
5899    ldes => 'LongDescription', #10
5900    pcst => { #7
5901        Name => 'Podcast',
5902        Format => 'int8u', #23
5903        Writable => 'int8s', #27
5904        PrintConv => { 0 => 'No', 1 => 'Yes' },
5905    },
5906    perf => 'Performer',
5907    plID => { #10 (or TV season)
5908        Name => 'PlayListID',
5909        Format => 'int8u',  # actually int64u, but split it up
5910        Writable => 'int32s', #27
5911    },
5912    purd => 'PurchaseDate', #7
5913    purl => 'PodcastURL', #7
5914    rtng => { #10
5915        Name => 'Rating',
5916        Format => 'int8u', #23
5917        Writable => 'int8s', #27
5918        PrintConv => {
5919            0 => 'none',
5920            1 => 'Explicit',
5921            2 => 'Clean',
5922            4 => 'Explicit (old)',
5923        },
5924    },
5925    sfID => { #10
5926        Name => 'AppleStoreCountry',
5927        Format => 'int32u',
5928        Writable => 'int32s', #27
5929        SeparateTable => 1,
5930        PrintConv => { #21
5931            143441 => 'United States', # US
5932            143442 => 'France', # FR
5933            143443 => 'Germany', # DE
5934            143444 => 'United Kingdom', # GB
5935            143445 => 'Austria', # AT
5936            143446 => 'Belgium', # BE
5937            143447 => 'Finland', # FI
5938            143448 => 'Greece', # GR
5939            143449 => 'Ireland', # IE
5940            143450 => 'Italy', # IT
5941            143451 => 'Luxembourg', # LU
5942            143452 => 'Netherlands', # NL
5943            143453 => 'Portugal', # PT
5944            143454 => 'Spain', # ES
5945            143455 => 'Canada', # CA
5946            143456 => 'Sweden', # SE
5947            143457 => 'Norway', # NO
5948            143458 => 'Denmark', # DK
5949            143459 => 'Switzerland', # CH
5950            143460 => 'Australia', # AU
5951            143461 => 'New Zealand', # NZ
5952            143462 => 'Japan', # JP
5953            143463 => 'Hong Kong', # HK
5954            143464 => 'Singapore', # SG
5955            143465 => 'China', # CN
5956            143466 => 'Republic of Korea', # KR
5957            143467 => 'India', # IN
5958            143468 => 'Mexico', # MX
5959            143469 => 'Russia', # RU
5960            143470 => 'Taiwan', # TW
5961            143471 => 'Vietnam', # VN
5962            143472 => 'South Africa', # ZA
5963            143473 => 'Malaysia', # MY
5964            143474 => 'Philippines', # PH
5965            143475 => 'Thailand', # TH
5966            143476 => 'Indonesia', # ID
5967            143477 => 'Pakistan', # PK
5968            143478 => 'Poland', # PL
5969            143479 => 'Saudi Arabia', # SA
5970            143480 => 'Turkey', # TR
5971            143481 => 'United Arab Emirates', # AE
5972            143482 => 'Hungary', # HU
5973            143483 => 'Chile', # CL
5974            143484 => 'Nepal', # NP
5975            143485 => 'Panama', # PA
5976            143486 => 'Sri Lanka', # LK
5977            143487 => 'Romania', # RO
5978            143489 => 'Czech Republic', # CZ
5979            143491 => 'Israel', # IL
5980            143492 => 'Ukraine', # UA
5981            143493 => 'Kuwait', # KW
5982            143494 => 'Croatia', # HR
5983            143495 => 'Costa Rica', # CR
5984            143496 => 'Slovakia', # SK
5985            143497 => 'Lebanon', # LB
5986            143498 => 'Qatar', # QA
5987            143499 => 'Slovenia', # SI
5988            143501 => 'Colombia', # CO
5989            143502 => 'Venezuela', # VE
5990            143503 => 'Brazil', # BR
5991            143504 => 'Guatemala', # GT
5992            143505 => 'Argentina', # AR
5993            143506 => 'El Salvador', # SV
5994            143507 => 'Peru', # PE
5995            143508 => 'Dominican Republic', # DO
5996            143509 => 'Ecuador', # EC
5997            143510 => 'Honduras', # HN
5998            143511 => 'Jamaica', # JM
5999            143512 => 'Nicaragua', # NI
6000            143513 => 'Paraguay', # PY
6001            143514 => 'Uruguay', # UY
6002            143515 => 'Macau', # MO
6003            143516 => 'Egypt', # EG
6004            143517 => 'Kazakhstan', # KZ
6005            143518 => 'Estonia', # EE
6006            143519 => 'Latvia', # LV
6007            143520 => 'Lithuania', # LT
6008            143521 => 'Malta', # MT
6009            143523 => 'Moldova', # MD
6010            143524 => 'Armenia', # AM
6011            143525 => 'Botswana', # BW
6012            143526 => 'Bulgaria', # BG
6013            143528 => 'Jordan', # JO
6014            143529 => 'Kenya', # KE
6015            143530 => 'Macedonia', # MK
6016            143531 => 'Madagascar', # MG
6017            143532 => 'Mali', # ML
6018            143533 => 'Mauritius', # MU
6019            143534 => 'Niger', # NE
6020            143535 => 'Senegal', # SN
6021            143536 => 'Tunisia', # TN
6022            143537 => 'Uganda', # UG
6023            143538 => 'Anguilla', # AI
6024            143539 => 'Bahamas', # BS
6025            143540 => 'Antigua and Barbuda', # AG
6026            143541 => 'Barbados', # BB
6027            143542 => 'Bermuda', # BM
6028            143543 => 'British Virgin Islands', # VG
6029            143544 => 'Cayman Islands', # KY
6030            143545 => 'Dominica', # DM
6031            143546 => 'Grenada', # GD
6032            143547 => 'Montserrat', # MS
6033            143548 => 'St. Kitts and Nevis', # KN
6034            143549 => 'St. Lucia', # LC
6035            143550 => 'St. Vincent and The Grenadines', # VC
6036            143551 => 'Trinidad and Tobago', # TT
6037            143552 => 'Turks and Caicos', # TC
6038            143553 => 'Guyana', # GY
6039            143554 => 'Suriname', # SR
6040            143555 => 'Belize', # BZ
6041            143556 => 'Bolivia', # BO
6042            143557 => 'Cyprus', # CY
6043            143558 => 'Iceland', # IS
6044            143559 => 'Bahrain', # BH
6045            143560 => 'Brunei Darussalam', # BN
6046            143561 => 'Nigeria', # NG
6047            143562 => 'Oman', # OM
6048            143563 => 'Algeria', # DZ
6049            143564 => 'Angola', # AO
6050            143565 => 'Belarus', # BY
6051            143566 => 'Uzbekistan', # UZ
6052            143568 => 'Azerbaijan', # AZ
6053            143571 => 'Yemen', # YE
6054            143572 => 'Tanzania', # TZ
6055            143573 => 'Ghana', # GH
6056            143575 => 'Albania', # AL
6057            143576 => 'Benin', # BJ
6058            143577 => 'Bhutan', # BT
6059            143578 => 'Burkina Faso', # BF
6060            143579 => 'Cambodia', # KH
6061            143580 => 'Cape Verde', # CV
6062            143581 => 'Chad', # TD
6063            143582 => 'Republic of the Congo', # CG
6064            143583 => 'Fiji', # FJ
6065            143584 => 'Gambia', # GM
6066            143585 => 'Guinea-Bissau', # GW
6067            143586 => 'Kyrgyzstan', # KG
6068            143587 => "Lao People's Democratic Republic", # LA
6069            143588 => 'Liberia', # LR
6070            143589 => 'Malawi', # MW
6071            143590 => 'Mauritania', # MR
6072            143591 => 'Federated States of Micronesia', # FM
6073            143592 => 'Mongolia', # MN
6074            143593 => 'Mozambique', # MZ
6075            143594 => 'Namibia', # NA
6076            143595 => 'Palau', # PW
6077            143597 => 'Papua New Guinea', # PG
6078            143598 => 'Sao Tome and Principe', # ST (S&atilde;o Tom&eacute; and Pr&iacute;ncipe)
6079            143599 => 'Seychelles', # SC
6080            143600 => 'Sierra Leone', # SL
6081            143601 => 'Solomon Islands', # SB
6082            143602 => 'Swaziland', # SZ
6083            143603 => 'Tajikistan', # TJ
6084            143604 => 'Turkmenistan', # TM
6085            143605 => 'Zimbabwe', # ZW
6086        },
6087    },
6088    soaa => 'SortAlbumArtist', #10
6089    soal => 'SortAlbum', #10
6090    soar => 'SortArtist', #10
6091    soco => 'SortComposer', #10
6092    sonm => 'SortName', #10
6093    sosn => 'SortShow', #10
6094    stik => { #10
6095        Name => 'MediaType',
6096        Format => 'int8u', #23
6097        Writable => 'int8s', #27
6098        PrintConvColumns => 2,
6099        PrintConv => { #(http://weblog.xanga.com/gryphondwb/615474010/iphone-ringtones---what-did-itunes-741-really-do.html)
6100            0 => 'Movie (old)', #forum9059 (was Movie)
6101            1 => 'Normal (Music)',
6102            2 => 'Audiobook',
6103            5 => 'Whacked Bookmark',
6104            6 => 'Music Video',
6105            9 => 'Movie', #forum9059 (was Short Film)
6106            10 => 'TV Show',
6107            11 => 'Booklet',
6108            14 => 'Ringtone',
6109            21 => 'Podcast', #15
6110            23 => 'iTunes U', #forum9059
6111        },
6112    },
6113    rate => 'RatingPercent', #PH
6114    titl => { Name => 'Title', Avoid => 1 },
6115    tven => 'TVEpisodeID', #7
6116    tves => { #7/10
6117        Name => 'TVEpisode',
6118        Format => 'int32u',
6119        Writable => 'int32s', #27
6120    },
6121    tvnn => 'TVNetworkName', #7
6122    tvsh => 'TVShow', #10
6123    tvsn => { #7/10
6124        Name => 'TVSeason',
6125        Format => 'int32u',
6126    },
6127    yrrc => 'Year', #(ffmpeg source)
6128    itnu => { #PH (iTunes 10.5)
6129        Name => 'iTunesU',
6130        Format => 'int8u', #27
6131        Writable => 'int8s', #27
6132        Description => 'iTunes U',
6133        PrintConv => { 0 => 'No', 1 => 'Yes' },
6134    },
6135    #https://github.com/communitymedia/mediautilities/blob/master/src/net/sourceforge/jaad/mp4/boxes/BoxTypes.java
6136    gshh => { Name => 'GoogleHostHeader',   Format => 'string' },
6137    gspm => { Name => 'GooglePingMessage',  Format => 'string' },
6138    gspu => { Name => 'GooglePingURL',      Format => 'string' },
6139    gssd => { Name => 'GoogleSourceData',   Format => 'string' },
6140    gsst => { Name => 'GoogleStartTime',    Format => 'string' },
6141    gstd => {
6142        Name => 'GoogleTrackDuration',
6143        Format => 'string',
6144        ValueConv => '$val / 1000',
6145        ValueConvInv => '$val * 1000',
6146        PrintConv => 'ConvertDuration($val)',
6147        PrintConvInv => q{
6148           $val =~ s/ s$//;
6149           my @a = split /(:| days )/, $val;
6150           my $sign = ($val =~ s/^-//) ? -1 : 1;
6151           $a[0] += shift(@a) * 24 if @a == 4;
6152           $a[0] += shift(@a) * 60 while @a > 1;
6153           return $a[0] * $sign;
6154        },
6155    },
6156
6157    # atoms observed in AAX audiobooks (ref PH)
6158    "\xa9cpy" => { Name => 'Copyright', Avoid => 1, Groups => { 2 => 'Author' } },
6159    "\xa9pub" => 'Publisher',
6160    "\xa9nrt" => 'Narrator',
6161    '@pti' => 'ParentTitle', # (guess -- same as "\xa9nam")
6162    '@PST' => 'ParentShortTitle', # (guess -- same as "\xa9nam")
6163    '@ppi' => 'ParentProductID', # (guess -- same as 'prID')
6164    '@sti' => 'ShortTitle', # (guess -- same as "\xa9nam")
6165    prID => 'ProductID',
6166    rldt => { Name => 'ReleaseDate', Groups => { 2 => 'Time' }},
6167    CDEK => { Name => 'Unknown_CDEK', Unknown => 1 }, # eg: "B004ZMTFEG" - used in URL's ("asin=")
6168    CDET => { Name => 'Unknown_CDET', Unknown => 1 }, # eg: "ADBL"
6169    VERS => 'ProductVersion',
6170    GUID => 'GUID',
6171    AACR => { Name => 'Unknown_AACR', Unknown => 1 }, # eg: "CR!1T1H1QH6WX7T714G2BMFX3E9MC4S"
6172    # ausr - 30 bytes (User Alias?)
6173    "\xa9xyz" => { #PH (written by Google Photos)
6174        Name => 'GPSCoordinates',
6175        Groups => { 2 => 'Location' },
6176        ValueConv => \&ConvertISO6709,
6177        ValueConvInv => \&ConvInvISO6709,
6178        PrintConv => \&PrintGPSCoordinates,
6179        PrintConvInv => \&PrintInvGPSCoordinates,
6180    },
6181    # the following tags written by iTunes 12.5.1.21
6182    # (ref https://www.ventismedia.com/mantis/view.php?id=14963
6183    #  https://community.mp3tag.de/t/x-mp4-new-tag-problems/19488)
6184    "\xa9wrk" => 'Work', #PH
6185    "\xa9mvn" => 'MovementName', #PH
6186    "\xa9mvi" => { #PH
6187        Name => 'MovementNumber',
6188        Format => 'int16u', #27
6189        Writable => 'int16s', #27
6190    },
6191    "\xa9mvc" => { #PH
6192        Name => 'MovementCount',
6193        Format => 'int16u', #27
6194        Writable => 'int16s', #27
6195    },
6196    shwm => { #PH
6197        Name => 'ShowMovement',
6198        Format => 'int8u', #27
6199        Writable => 'int8s', #27
6200        PrintConv => { 0 => 'No', 1 => 'Yes' },
6201    },
6202);
6203
6204# tag decoded from timed face records
6205%Image::ExifTool::QuickTime::FaceInfo = (
6206    PROCESS_PROC => \&ProcessMOV,
6207    GROUPS => { 2 => 'Video' },
6208    crec => {
6209        Name => 'FaceRec',
6210        SubDirectory => {
6211            TagTable => 'Image::ExifTool::QuickTime::FaceRec',
6212        },
6213    },
6214);
6215
6216# tag decoded from timed face records
6217%Image::ExifTool::QuickTime::FaceRec = (
6218    PROCESS_PROC => \&ProcessMOV,
6219    GROUPS => { 2 => 'Video' },
6220    cits => {
6221        Name => 'FaceItem',
6222        SubDirectory => {
6223            TagTable => 'Image::ExifTool::QuickTime::Keys',
6224            ProcessProc => \&Process_mebx,
6225        },
6226    },
6227);
6228
6229# item list keys (ref PH)
6230%Image::ExifTool::QuickTime::Keys = (
6231    PROCESS_PROC => \&ProcessKeys,
6232    WRITE_PROC => \&WriteKeys,
6233    CHECK_PROC => \&CheckQTValue,
6234    VARS => { LONG_TAGS => 7 },
6235    WRITABLE => 1,
6236    # (not PREFERRED when writing)
6237    GROUPS => { 1 => 'Keys' },
6238    WRITE_GROUP => 'Keys',
6239    LANG_INFO => \&GetLangInfo,
6240    NOTES => q{
6241        This directory contains a list of key names which are used to decode tags
6242        written by the "mdta" handler.  Also in this table are a few tags found in
6243        timed metadata that are not yet writable by ExifTool.  The prefix of
6244        "com.apple.quicktime." has been removed from the TagID's below.  These tags
6245        support alternate languages in the same way as the
6246        L<ItemList|Image::ExifTool::TagNames/QuickTime ItemList Tags> tags.  Note
6247        that by default,
6248        L<ItemList|Image::ExifTool::TagNames/QuickTime ItemList Tags> and
6249        L<UserData|Image::ExifTool::TagNames/QuickTime UserData Tags> tags are
6250        preferred when writing, so to create a tag when a same-named tag exists in
6251        either of these tables, either the "Keys" location must be specified (eg.
6252        C<-Keys:Author=Phil> on the command line), or the PREFERRED level must be
6253        changed via L<the config file|../config.html#PREF>.
6254    },
6255    version     => 'Version',
6256    album       => 'Album',
6257    artist      => { },
6258    artwork     => { },
6259    author      => { Name => 'Author',      Groups => { 2 => 'Author' } },
6260    comment     => { },
6261    copyright   => { Name => 'Copyright',   Groups => { 2 => 'Author' } },
6262    creationdate=> {
6263        Name => 'CreationDate',
6264        Groups => { 2 => 'Time' },
6265        Shift => 'Time',
6266        ValueConv => q{
6267            require Image::ExifTool::XMP;
6268            $val =  Image::ExifTool::XMP::ConvertXMPDate($val,1);
6269            $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
6270            return $val;
6271        },
6272        ValueConvInv => q{
6273            require Image::ExifTool::XMP;
6274            $val =  Image::ExifTool::XMP::FormatXMPDate($val);
6275            $val =~ s/([-+]\d{2}):(\d{2})$/$1$2/; # remove time zone colon
6276            return $val;
6277        },
6278        PrintConv => '$self->ConvertDateTime($val)',
6279        PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
6280    },
6281    description => { },
6282    director    => { },
6283    displayname => { Name => 'DisplayName' },
6284    title       => { }, #22
6285    genre       => { },
6286    information => { },
6287    keywords    => { },
6288    producer    => { }, #22
6289    make        => { Name => 'Make',        Groups => { 2 => 'Camera' } },
6290    model       => { Name => 'Model',       Groups => { 2 => 'Camera' } },
6291    publisher   => { },
6292    software    => { },
6293    year        => { Groups => { 2 => 'Time' } },
6294    'camera.identifier' => 'CameraIdentifier', # (iPhone 4)
6295    'camera.framereadouttimeinmicroseconds' => { # (iPhone 4)
6296        Name => 'FrameReadoutTime',
6297        ValueConv => '$val * 1e-6',
6298        ValueConvInv => 'int($val * 1e6 + 0.5)',
6299        PrintConv => '$val * 1e6 . " microseconds"',
6300        PrintConvInv => '$val =~ s/ .*//; $val * 1e-6',
6301    },
6302    'location.ISO6709' => {
6303        Name => 'GPSCoordinates',
6304        Groups => { 2 => 'Location' },
6305        Notes => q{
6306            Google Photos may ignore this if the coorinates have more than 5 digits
6307            after the decimal
6308        },
6309        ValueConv => \&ConvertISO6709,
6310        ValueConvInv => \&ConvInvISO6709,
6311        PrintConv => \&PrintGPSCoordinates,
6312        PrintConvInv => \&PrintInvGPSCoordinates,
6313    },
6314    'location.name' => { Name => 'LocationName', Groups => { 2 => 'Location' } },
6315    'location.body' => { Name => 'LocationBody', Groups => { 2 => 'Location' } },
6316    'location.note' => { Name => 'LocationNote', Groups => { 2 => 'Location' } },
6317    'location.role' => {
6318        Name => 'LocationRole',
6319        Groups => { 2 => 'Location' },
6320        PrintConv => {
6321            0 => 'Shooting Location',
6322            1 => 'Real Location',
6323            2 => 'Fictional Location',
6324        },
6325    },
6326    'location.date' => {
6327        Name => 'LocationDate',
6328        Groups => { 2 => 'Time' },
6329        Shift => 'Time',
6330        ValueConv => q{
6331            require Image::ExifTool::XMP;
6332            $val =  Image::ExifTool::XMP::ConvertXMPDate($val);
6333            $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
6334            return $val;
6335        },
6336        ValueConvInv => q{
6337            require Image::ExifTool::XMP;
6338            $val =  Image::ExifTool::XMP::FormatXMPDate($val);
6339            $val =~ s/([-+]\d{2}):(\d{2})$/$1$2/; # remove time zone colon
6340            return $val;
6341        },
6342        PrintConv => '$self->ConvertDateTime($val)',
6343        PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
6344    },
6345    'location.accuracy.horizontal' => { Name => 'LocationAccuracyHorizontal' },
6346    'live-photo.auto'           => { Name => 'LivePhotoAuto', Writable => 'int8u' },
6347    'live-photo.vitality-score' => { Name => 'LivePhotoVitalityScore', Writable => 'float' },
6348    'live-photo.vitality-scoring-version' => { Name => 'LivePhotoVitalityScoringVersion', Writable => 'int64s' },
6349    'apple.photos.variation-identifier'   => { Name => 'ApplePhotosVariationIdentifier',  Writable => 'int64s' },
6350    'direction.facing' => { Name => 'CameraDirection', Groups => { 2 => 'Location' } },
6351    'direction.motion' => { Name => 'CameraMotion',    Groups => { 2 => 'Location' } },
6352    'location.body'    => { Name => 'LocationBody',    Groups => { 2 => 'Location' } },
6353    'player.version'                => 'PlayerVersion',
6354    'player.movie.visual.brightness'=> 'Brightness',
6355    'player.movie.visual.color'     => 'Color',
6356    'player.movie.visual.tint'      => 'Tint',
6357    'player.movie.visual.contrast'  => 'Contrast',
6358    'player.movie.audio.gain'       => 'AudioGain',
6359    'player.movie.audio.treble'     => 'Trebel',
6360    'player.movie.audio.bass'       => 'Bass',
6361    'player.movie.audio.balance'    => 'Balance',
6362    'player.movie.audio.pitchshift' => 'PitchShift',
6363    'player.movie.audio.mute' => {
6364        Name => 'Mute',
6365        Format => 'int8u',
6366        PrintConv => { 0 => 'Off', 1 => 'On' },
6367    },
6368    'rating.user'  => 'UserRating', # (Canon ELPH 510 HS)
6369    'collection.user' => 'UserCollection', #22
6370    'Encoded_With' => 'EncodedWith',
6371#
6372# the following tags aren't in the com.apple.quicktime namespace:
6373#
6374    'com.apple.photos.captureMode' => 'CaptureMode',
6375    'com.android.version' => 'AndroidVersion',
6376#
6377# also seen
6378#
6379    # com.divergentmedia.clipwrap.model            ('NEX-FS700EK')
6380    # com.divergentmedia.clipwrap.model1           ('49')
6381    # com.divergentmedia.clipwrap.model2           ('0')
6382    # com.divergentmedia.clipwrap.manufacturer     ('Sony')
6383    # com.divergentmedia.clipwrap.originalDateTime ('2013/2/6 10:30:40+0200')
6384#
6385# seen in timed metadata (mebx), and added dynamically to the table via SaveMetaKeys()
6386# NOTE: these tags are not writable! (timed metadata cannot yet be written)
6387#
6388    # (mdta)com.apple.quicktime.video-orientation (dtyp=66, int16s)
6389    'video-orientation' => {
6390        Name => 'VideoOrientation',
6391        Writable => 0,
6392        PrintConv => \%Image::ExifTool::Exif::orientation, #PH (NC)
6393    },
6394    # (mdta)com.apple.quicktime.live-photo-info (dtyp=com.apple.quicktime.com.apple.quicktime.live-photo-info)
6395    'live-photo-info' => {
6396        Name => 'LivePhotoInfo',
6397        Writable => 0,
6398        # not sure what these values mean, but unpack them anyway - PH
6399        # (ignore the fact that the "f" and "l" unpacks won't work on a big-endian machine)
6400        ValueConv => 'join " ",unpack "VfVVf6c4lCCcclf4Vvv", $val',
6401    },
6402    # (mdta)com.apple.quicktime.still-image-time (dtyp=65, int8s)
6403    'still-image-time' => { # (found in live photo)
6404        Name => 'StillImageTime',
6405        Writable => 0,
6406        Notes => q{
6407            this tag always has a value of -1; the time of the still image is obtained
6408            from the associated SampleTime
6409        },
6410    },
6411    # (mdta)com.apple.quicktime.detected-face (dtyp='com.apple.quicktime.detected-face')
6412    'detected-face' => {
6413        Name => 'FaceInfo',
6414        Writable => 0,
6415        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::FaceInfo' },
6416    },
6417    # ---- detected-face fields ( ----
6418    # --> back here after a round trip through FaceInfo -> FaceRec -> FaceItem
6419    # (fiel)com.apple.quicktime.detected-face.bounds (dtyp=80, float[8])
6420    'detected-face.bounds' => {
6421        Name => 'DetectedFaceBounds',
6422        Writable => 0,
6423        # round to a reasonable number of decimal places
6424        PrintConv => 'my @a=split " ",$val;$_=int($_*1e6+.5)/1e6 foreach @a;join " ",@a',
6425        PrintConvInv => '$val',
6426    },
6427    # (fiel)com.apple.quicktime.detected-face.face-id (dtyp=77, int32u)
6428    'detected-face.face-id'    => { Name => 'DetectedFaceID',        Writable => 0 },
6429    # (fiel)com.apple.quicktime.detected-face.roll-angle (dtyp=23, float)
6430    'detected-face.roll-angle' => { Name => 'DetectedFaceRollAngle', Writable => 0 },
6431    # (fiel)com.apple.quicktime.detected-face.yaw-angle (dtyp=23, float)
6432    'detected-face.yaw-angle'  => { Name => 'DetectedFaceYawAngle',  Writable => 0 },
6433#
6434# seen in Apple ProRes RAW file
6435#
6436    # (mdta)com.apple.proapps.manufacturer (eg. "Sony")
6437    # (mdta)com.apple.proapps.exif.{Exif}.FNumber (float, eg. 1.0)
6438    # (mdta)org.smpte.rdd18.lens.irisfnumber (eg. "F1.0")
6439    # (mdta)com.apple.proapps.exif.{Exif}.ShutterSpeedValue (float, eg. 1.006)
6440    # (mdta)org.smpte.rdd18.camera.shutterspeed_angle (eg. "179.2deg")
6441    # (mdta)org.smpte.rdd18.camera.neutraldensityfilterwheelsetting (eg. "ND1")
6442    # (mdta)org.smpte.rdd18.camera.whitebalance (eg. "4300K")
6443    # (mdta)com.apple.proapps.exif.{Exif}.ExposureIndex (float, eg. 4000)
6444    # (mdta)org.smpte.rdd18.camera.isosensitivity (eg. "4000")
6445    # (mdta)com.apple.proapps.image.{TIFF}.Make (eg. "Atmos")
6446    # (mdta)com.apple.proapps.image.{TIFF}.Model (eg. "ShogunInferno")
6447    # (mdta)com.apple.proapps.image.{TIFF}.Software (eg. "9.0")
6448);
6449
6450# iTunes info ('----') atoms
6451%Image::ExifTool::QuickTime::iTunesInfo = (
6452    PROCESS_PROC => \&ProcessMOV,
6453    GROUPS => { 2 => 'Audio' },
6454    NOTES => q{
6455        ExifTool will extract any iTunesInfo tags that exist, even if they are not
6456        defined in this table.
6457    },
6458    # 'mean'/'name'/'data' atoms form a triplet, but unfortunately
6459    # I haven't been able to find any documentation on this.
6460    # 'mean' is normally 'com.apple.iTunes'
6461    mean => {
6462        Name => 'Mean',
6463        # the 'Triplet' flag tells ProcessMOV() to generate
6464        # a single tag from the mean/name/data triplet
6465        Triplet => 1,
6466        Hidden => 1,
6467    },
6468    name => {
6469        Name => 'Name',
6470        Triplet => 1,
6471        Hidden => 1,
6472    },
6473    data => {
6474        Name => 'Data',
6475        Triplet => 1,
6476        Hidden => 1,
6477    },
6478    # the tag ID's below are composed from "mean/name",
6479    # but "mean/" is omitted if it is "com.apple.iTunes/":
6480    'iTunMOVI' => {
6481        Name => 'iTunMOVI',
6482        SubDirectory => { TagTable => 'Image::ExifTool::PLIST::Main' },
6483    },
6484    'tool' => {
6485        Name => 'iTunTool',
6486        Description => 'iTunTool',
6487        Format => 'int32u',
6488        PrintConv => 'sprintf("0x%.8x",$val)',
6489    },
6490    'iTunEXTC' => {
6491        Name => 'ContentRating',
6492        Notes => 'standard | rating | score | reasons',
6493        # eg. 'us-tv|TV-14|500|V', 'mpaa|PG-13|300|For violence and sexuality'
6494        # (see http://shadowofged.blogspot.ca/2008/06/itunes-content-ratings.html)
6495    },
6496    'iTunNORM' => {
6497        Name => 'VolumeNormalization',
6498        PrintConv => '$val=~s/ 0+(\w)/ $1/g; $val=~s/^\s+//; $val',
6499    },
6500    'iTunSMPB' => {
6501        Name => 'iTunSMPB',
6502        Description => 'iTunSMPB',
6503        # hex format, similar to iTunNORM, but 12 words instead of 10,
6504        # and 4th word is 16 hex digits (all others are 8)
6505        # (gives AAC encoder delay, ref http://code.google.com/p/l-smash/issues/detail?id=1)
6506        PrintConv => '$val=~s/ 0+(\w)/ $1/g; $val=~s/^\s+//; $val',
6507    },
6508    # (CDDB = Compact Disc DataBase)
6509    # iTunes_CDDB_1 = <CDDB1 disk ID>+<# tracks>+<logical block address for each track>...
6510    'iTunes_CDDB_1' => 'CDDB1Info',
6511    'iTunes_CDDB_TrackNumber' => 'CDDBTrackNumber',
6512    'Encoding Params' => {
6513        Name => 'EncodingParams',
6514        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::EncodingParams' },
6515    },
6516    # also heard about 'iTunPGAP', but I haven't seen a sample
6517    DISCNUMBER => 'DiscNumber', #PH
6518    TRACKNUMBER => 'TrackNumber', #PH
6519    popularimeter => 'Popularimeter', #PH
6520);
6521
6522# iTunes audio encoding parameters
6523# ref https://developer.apple.com/library/mac/#documentation/MusicAudio/Reference/AudioCodecServicesRef/Reference/reference.html
6524%Image::ExifTool::QuickTime::EncodingParams = (
6525    PROCESS_PROC => \&ProcessEncodingParams,
6526    GROUPS => { 2 => 'Audio' },
6527    # (I have commented out the ones that don't have integer values because they
6528    #  probably don't appear, and definitely wouldn't work with current decoding - PH)
6529
6530    # global codec properties
6531    #'lnam' => 'AudioCodecName',
6532    #'lmak' => 'AudioCodecManufacturer',
6533    #'lfor' => 'AudioCodecFormat',
6534    'vpk?' => 'AudioHasVariablePacketByteSizes',
6535    #'ifm#' => 'AudioSupportedInputFormats',
6536    #'ofm#' => 'AudioSupportedOutputFormats',
6537    #'aisr' => 'AudioAvailableInputSampleRates',
6538    #'aosr' => 'AudioAvailableOutputSampleRates',
6539    'abrt' => 'AudioAvailableBitRateRange',
6540    'mnip' => 'AudioMinimumNumberInputPackets',
6541    'mnop' => 'AudioMinimumNumberOutputPackets',
6542    'cmnc' => 'AudioAvailableNumberChannels',
6543    'lmrc' => 'AudioDoesSampleRateConversion',
6544    #'aicl' => 'AudioAvailableInputChannelLayoutTags',
6545    #'aocl' => 'AudioAvailableOutputChannelLayoutTags',
6546    #'if4o' => 'AudioInputFormatsForOutputFormat',
6547    #'of4i' => 'AudioOutputFormatsForInputFormat',
6548    #'acfi' => 'AudioFormatInfo',
6549
6550    # instance codec properties
6551    'tbuf' => 'AudioInputBufferSize',
6552    'pakf' => 'AudioPacketFrameSize',
6553    'pakb' => 'AudioMaximumPacketByteSize',
6554    #'ifmt' => 'AudioCurrentInputFormat',
6555    #'ofmt' => 'AudioCurrentOutputFormat',
6556    #'kuki' => 'AudioMagicCookie',
6557    'ubuf' => 'AudioUsedInputBufferSize',
6558    'init' => 'AudioIsInitialized',
6559    'brat' => 'AudioCurrentTargetBitRate',
6560    #'cisr' => 'AudioCurrentInputSampleRate',
6561    #'cosr' => 'AudioCurrentOutputSampleRate',
6562    'srcq' => 'AudioQualitySetting',
6563    #'brta' => 'AudioApplicableBitRateRange',
6564    #'isra' => 'AudioApplicableInputSampleRates',
6565    #'osra' => 'AudioApplicableOutputSampleRates',
6566    'pad0' => 'AudioZeroFramesPadded',
6567    'prmm' => 'AudioCodecPrimeMethod',
6568    #'prim' => 'AudioCodecPrimeInfo',
6569    #'icl ' => 'AudioInputChannelLayout',
6570    #'ocl ' => 'AudioOutputChannelLayout',
6571    #'acs ' => 'AudioCodecSettings',
6572    #'acfl' => 'AudioCodecFormatList',
6573    'acbf' => 'AudioBitRateControlMode',
6574    'vbrq' => 'AudioVBRQuality',
6575    'mdel' => 'AudioMinimumDelayMode',
6576
6577    # deprecated
6578    'pakd' => 'AudioRequiresPacketDescription',
6579    #'brt#' => 'AudioAvailableBitRates',
6580    'acef' => 'AudioExtendFrequencies',
6581    'ursr' => 'AudioUseRecommendedSampleRate',
6582    'oppr' => 'AudioOutputPrecedence',
6583    #'loud' => 'AudioCurrentLoudnessStatistics',
6584
6585    # others
6586    'vers' => 'AudioEncodingParamsVersion', #PH
6587    'cdcv' => { #PH
6588        Name => 'AudioComponentVersion',
6589        ValueConv => 'join ".", unpack("ncc", pack("N",$val))',
6590    },
6591);
6592
6593# print to video data block
6594%Image::ExifTool::QuickTime::Video = (
6595    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
6596    GROUPS => { 2 => 'Video' },
6597    0 => {
6598        Name => 'DisplaySize',
6599        PrintConv => {
6600            0 => 'Normal',
6601            1 => 'Double Size',
6602            2 => 'Half Size',
6603            3 => 'Full Screen',
6604            4 => 'Current Size',
6605        },
6606    },
6607    6 => {
6608        Name => 'SlideShow',
6609        PrintConv => {
6610            0 => 'No',
6611            1 => 'Yes',
6612        },
6613    },
6614);
6615
6616# 'hnti' atoms
6617%Image::ExifTool::QuickTime::HintInfo = (
6618    PROCESS_PROC => \&ProcessMOV,
6619    GROUPS => { 2 => 'Video' },
6620    'rtp ' => {
6621        Name => 'RealtimeStreamingProtocol',
6622        PrintConv => '$val=~s/^sdp /(SDP) /; $val',
6623    },
6624    'sdp ' => 'StreamingDataProtocol',
6625);
6626
6627# 'hinf' atoms
6628%Image::ExifTool::QuickTime::HintTrackInfo = (
6629    PROCESS_PROC => \&ProcessMOV,
6630    GROUPS => { 2 => 'Video' },
6631    trpY => { Name => 'TotalBytes', Format => 'int64u' }, #(documented)
6632    trpy => { Name => 'TotalBytes', Format => 'int64u' }, #(observed)
6633    totl => { Name => 'TotalBytes', Format => 'int32u' },
6634    nump => { Name => 'NumPackets', Format => 'int64u' },
6635    npck => { Name => 'NumPackets', Format => 'int32u' },
6636    tpyl => { Name => 'TotalBytesNoRTPHeaders', Format => 'int64u' },
6637    tpaY => { Name => 'TotalBytesNoRTPHeaders', Format => 'int32u' }, #(documented)
6638    tpay => { Name => 'TotalBytesNoRTPHeaders', Format => 'int32u' }, #(observed)
6639    maxr => {
6640        Name => 'MaxDataRate',
6641        Format => 'int32u',
6642        Count => 2,
6643        PrintConv => 'my @a=split(" ",$val);sprintf("%d bytes in %.3f s",$a[1],$a[0]/1000)',
6644    },
6645    dmed => { Name => 'MediaTrackBytes',    Format => 'int64u' },
6646    dimm => { Name => 'ImmediateDataBytes', Format => 'int64u' },
6647    drep => { Name => 'RepeatedDataBytes',  Format => 'int64u' },
6648    tmin => {
6649        Name => 'MinTransmissionTime',
6650        Format => 'int32u',
6651        PrintConv => 'sprintf("%.3f s",$val/1000)',
6652    },
6653    tmax => {
6654        Name => 'MaxTransmissionTime',
6655        Format => 'int32u',
6656        PrintConv => 'sprintf("%.3f s",$val/1000)',
6657    },
6658    pmax => { Name => 'LargestPacketSize',  Format => 'int32u' },
6659    dmax => {
6660        Name => 'LargestPacketDuration',
6661        Format => 'int32u',
6662        PrintConv => 'sprintf("%.3f s",$val/1000)',
6663    },
6664    payt => {
6665        Name => 'PayloadType',
6666        Format => 'undef',  # (necessary to prevent decoding as string!)
6667        ValueConv => 'unpack("N",$val) . " " . substr($val, 5)',
6668        PrintConv => '$val=~s/ /, /;$val',
6669    },
6670);
6671
6672# MP4 media box (ref 5)
6673%Image::ExifTool::QuickTime::Media = (
6674    PROCESS_PROC => \&ProcessMOV,
6675    WRITE_PROC => \&WriteQuickTime,
6676    GROUPS => { 1 => 'Track#', 2 => 'Video' },
6677    NOTES => 'MP4 media box.',
6678    mdhd => {
6679        Name => 'MediaHeader',
6680        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MediaHeader' },
6681    },
6682    hdlr => {
6683        Name => 'Handler',
6684        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Handler' },
6685    },
6686    minf => {
6687        Name => 'MediaInfo',
6688        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MediaInfo' },
6689    },
6690);
6691
6692# MP4 media header box (ref 5)
6693%Image::ExifTool::QuickTime::MediaHeader = (
6694    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
6695    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
6696    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
6697    GROUPS => { 1 => 'Track#', 2 => 'Video' },
6698    FORMAT => 'int32u',
6699    DATAMEMBER => [ 0, 1, 2, 3, 4 ],
6700    0 => {
6701        Name => 'MediaHeaderVersion',
6702        RawConv => '$$self{MediaHeaderVersion} = $val',
6703    },
6704    1 => {
6705        Name => 'MediaCreateDate',
6706        Groups => { 2 => 'Time' },
6707        %timeInfo,
6708        # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
6709        Hook => '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
6710    },
6711    2 => {
6712        Name => 'MediaModifyDate',
6713        Groups => { 2 => 'Time' },
6714        %timeInfo,
6715        # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
6716        Hook => '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
6717    },
6718    3 => {
6719        Name => 'MediaTimeScale',
6720        RawConv => '$$self{MediaTS} = $val',
6721    },
6722    4 => {
6723        Name => 'MediaDuration',
6724        RawConv => '$$self{MediaTS} ? $val / $$self{MediaTS} : $val',
6725        PrintConv => '$$self{MediaTS} ? ConvertDuration($val) : $val',
6726        # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
6727        Hook => '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
6728    },
6729    5 => {
6730        Name => 'MediaLanguageCode',
6731        Format => 'int16u',
6732        RawConv => '$val ? $val : undef',
6733        # allow both Macintosh (for MOV files) and ISO (for MP4 files) language codes
6734        ValueConv => '($val < 0x400 or $val == 0x7fff) ? $val : pack "C*", map { (($val>>$_)&0x1f)+0x60 } 10, 5, 0',
6735        PrintConv => q{
6736            return $val unless $val =~ /^\d+$/;
6737            require Image::ExifTool::Font;
6738            return $Image::ExifTool::Font::ttLang{Macintosh}{$val} || "Unknown ($val)";
6739        },
6740    },
6741);
6742
6743# MP4 media information box (ref 5)
6744%Image::ExifTool::QuickTime::MediaInfo = (
6745    PROCESS_PROC => \&ProcessMOV,
6746    WRITE_PROC => \&WriteQuickTime,
6747    GROUPS => { 1 => 'Track#', 2 => 'Video' },
6748    NOTES => 'MP4 media info box.',
6749    vmhd => {
6750        Name => 'VideoHeader',
6751        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::VideoHeader' },
6752    },
6753    smhd => {
6754        Name => 'AudioHeader',
6755        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AudioHeader' },
6756    },
6757    hmhd => {
6758        Name => 'HintHeader',
6759        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HintHeader' },
6760    },
6761    nmhd => {
6762        Name => 'NullMediaHeader',
6763        Flags => ['Binary','Unknown'],
6764    },
6765    dinf => {
6766        Name => 'DataInfo', # (don't change this name -- used to recognize directory when writing)
6767        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::DataInfo' },
6768    },
6769    gmhd => {
6770        Name => 'GenMediaHeader',
6771        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::GenMediaHeader' },
6772    },
6773    hdlr => { #PH
6774        Name => 'Handler',
6775        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Handler' },
6776    },
6777    stbl => {
6778        Name => 'SampleTable',
6779        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SampleTable' },
6780    },
6781);
6782
6783# MP4 video media header (ref 5)
6784%Image::ExifTool::QuickTime::VideoHeader = (
6785    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
6786    GROUPS => { 2 => 'Video' },
6787    NOTES => 'MP4 video media header.',
6788    FORMAT => 'int16u',
6789    2 => {
6790        Name => 'GraphicsMode',
6791        PrintHex => 1,
6792        SeparateTable => 'GraphicsMode',
6793        PrintConv => \%graphicsMode,
6794    },
6795    3 => { Name => 'OpColor', Format => 'int16u[3]' },
6796);
6797
6798# MP4 audio media header (ref 5)
6799%Image::ExifTool::QuickTime::AudioHeader = (
6800    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
6801    GROUPS => { 2 => 'Audio' },
6802    NOTES => 'MP4 audio media header.',
6803    FORMAT => 'int16u',
6804    2 => { Name => 'Balance', Format => 'fixed16s' },
6805);
6806
6807# MP4 hint media header (ref 5)
6808%Image::ExifTool::QuickTime::HintHeader = (
6809    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
6810    NOTES => 'MP4 hint media header.',
6811    FORMAT => 'int16u',
6812    2 => 'MaxPDUSize',
6813    3 => 'AvgPDUSize',
6814    4 => { Name => 'MaxBitrate', Format => 'int32u', PrintConv => 'ConvertBitrate($val)' },
6815    6 => { Name => 'AvgBitrate', Format => 'int32u', PrintConv => 'ConvertBitrate($val)' },
6816);
6817
6818# MP4 sample table box (ref 5)
6819%Image::ExifTool::QuickTime::SampleTable = (
6820    PROCESS_PROC => \&ProcessMOV,
6821    WRITE_PROC => \&WriteQuickTime,
6822    GROUPS => { 2 => 'Video' },
6823    NOTES => 'MP4 sample table box.',
6824    stsd => [
6825        {
6826            Name => 'AudioSampleDesc',
6827            Condition => '$$self{HandlerType} and $$self{HandlerType} eq "soun"',
6828            SubDirectory => {
6829                TagTable => 'Image::ExifTool::QuickTime::AudioSampleDesc',
6830                ProcessProc => \&ProcessSampleDesc,
6831            },
6832        },{
6833            Name => 'VideoSampleDesc',
6834            Condition => '$$self{HandlerType} and $$self{HandlerType} eq "vide"',
6835            SubDirectory => {
6836                TagTable => 'Image::ExifTool::QuickTime::ImageDesc',
6837                ProcessProc => \&ProcessSampleDesc,
6838            },
6839        },{
6840            Name => 'HintSampleDesc',
6841            Condition => '$$self{HandlerType} and $$self{HandlerType} eq "hint"',
6842            SubDirectory => {
6843                TagTable => 'Image::ExifTool::QuickTime::HintSampleDesc',
6844                ProcessProc => \&ProcessSampleDesc,
6845            },
6846        },{
6847            Name => 'MetaSampleDesc',
6848            Condition => '$$self{HandlerType} and $$self{HandlerType} eq "meta"',
6849            SubDirectory => {
6850                TagTable => 'Image::ExifTool::QuickTime::MetaSampleDesc',
6851                ProcessProc => \&ProcessSampleDesc,
6852            },
6853        },{
6854            Name => 'OtherSampleDesc',
6855            SubDirectory => {
6856                TagTable => 'Image::ExifTool::QuickTime::OtherSampleDesc',
6857                ProcessProc => \&ProcessSampleDesc,
6858            },
6859        },
6860        # (Note: "alis" HandlerType handled by the parent audio or video handler)
6861    ],
6862    stts => [ # decoding time-to-sample table
6863        {
6864            Name => 'VideoFrameRate',
6865            Notes => 'average rate calculated from time-to-sample table for video media',
6866            Condition => '$$self{HandlerType} and $$self{HandlerType} eq "vide"',
6867            Format => 'undef',  # (necessary to prevent decoding as string!)
6868            # (must be RawConv so appropriate MediaTS is used in calculation)
6869            RawConv => 'Image::ExifTool::QuickTime::CalcSampleRate($self, \$val)',
6870            PrintConv => 'int($val * 1000 + 0.5) / 1000',
6871        },
6872        {
6873            Name => 'TimeToSampleTable',
6874            Flags => ['Binary','Unknown'],
6875        },
6876    ],
6877    ctts => {
6878        Name => 'CompositionTimeToSample',
6879        Flags => ['Binary','Unknown'],
6880    },
6881    stsc => {
6882        Name => 'SampleToChunk',
6883        Flags => ['Binary','Unknown'],
6884    },
6885    stsz => {
6886        Name => 'SampleSizes',
6887        Flags => ['Binary','Unknown'],
6888    },
6889    stz2 => {
6890        Name => 'CompactSampleSizes',
6891        Flags => ['Binary','Unknown'],
6892    },
6893    stco => {
6894        Name => 'ChunkOffset',
6895        Flags => ['Binary','Unknown'],
6896    },
6897    co64 => {
6898        Name => 'ChunkOffset64',
6899        Flags => ['Binary','Unknown'],
6900    },
6901    stss => {
6902        Name => 'SyncSampleTable',
6903        Flags => ['Binary','Unknown'],
6904    },
6905    stsh => {
6906        Name => 'ShadowSyncSampleTable',
6907        Flags => ['Binary','Unknown'],
6908    },
6909    padb => {
6910        Name => 'SamplePaddingBits',
6911        Flags => ['Binary','Unknown'],
6912    },
6913    stdp => {
6914        Name => 'SampleDegradationPriority',
6915        Flags => ['Binary','Unknown'],
6916    },
6917    sdtp => {
6918        Name => 'IdependentAndDisposableSamples',
6919        Flags => ['Binary','Unknown'],
6920    },
6921    sbgp => {
6922        Name => 'SampleToGroup',
6923        Flags => ['Binary','Unknown'],
6924    },
6925    sgpd => {
6926        Name => 'SampleGroupDescription',
6927        Flags => ['Binary','Unknown'],
6928        # bytes 4-7 give grouping type (ref ISO/IEC 14496-15:2014)
6929        #   tsas - temporal sublayer sample
6930        #   stsa - step-wise temporal layer access
6931        #   avss - AVC sample
6932        #   tscl - temporal layer scalability
6933        #   sync - sync sample
6934    },
6935    subs => {
6936        Name => 'Sub-sampleInformation',
6937        Flags => ['Binary','Unknown'],
6938    },
6939    cslg => {
6940        Name => 'CompositionToDecodeTimelineMapping',
6941        Flags => ['Binary','Unknown'],
6942    },
6943    stps => {
6944        Name => 'PartialSyncSamples',
6945        ValueConv => 'join " ",unpack("x8N*",$val)',
6946    },
6947    # mark - 8 bytes all zero (GoPro)
6948);
6949
6950# MP4 audio sample description box (ref 5/AtomicParsley 0.9.4 parsley.cpp)
6951%Image::ExifTool::QuickTime::AudioSampleDesc = (
6952    PROCESS_PROC => \&ProcessHybrid,
6953    VARS => { ID_LABEL => 'ID/Index' },
6954    GROUPS => { 2 => 'Audio' },
6955    NOTES => q{
6956        MP4 audio sample description.  This hybrid atom contains both data and child
6957        atoms.
6958    },
6959    4  => {
6960        Name => 'AudioFormat',
6961        Format => 'undef[4]',
6962        RawConv => q{
6963            $$self{AudioFormat} = $val;
6964            return undef unless $val =~ /^[\w ]{4}$/i;
6965            # check for protected audio format
6966            $self->OverrideFileType('M4P') if $val eq 'drms' and $$self{VALUE}{FileType} eq 'M4A';
6967            return $val;
6968        },
6969        # see this link for print conversions (not complete):
6970        # https://github.com/yannickcr/brooser/blob/master/php/librairies/getid3/module.audio-video.quicktime.php
6971    },
6972    20 => { #PH
6973        Name => 'AudioVendorID',
6974        Condition => '$$self{AudioFormat} ne "mp4s"',
6975        Format => 'undef[4]',
6976        RawConv => '$val eq "\0\0\0\0" ? undef : $val',
6977        PrintConv => \%vendorID,
6978        SeparateTable => 'VendorID',
6979    },
6980    24 => { Name => 'AudioChannels',        Format => 'int16u' },
6981    26 => { Name => 'AudioBitsPerSample',   Format => 'int16u' },
6982    32 => { Name => 'AudioSampleRate',      Format => 'fixed32u' },
6983#
6984# Observed offsets for child atoms of various AudioFormat types:
6985#
6986#   AudioFormat  Offset  Child atoms
6987#   -----------  ------  ----------------
6988#   mp4a         52 *    wave, chan, esds, SA3D(Insta360 spherical video params?,also GoPro Max)
6989#   in24         52      wave, chan
6990#   "ms\0\x11"   52      wave
6991#   sowt         52      chan
6992#   mp4a         36 *    esds, pinf
6993#   drms         36      esds, sinf
6994#   samr         36      damr
6995#   alac         36      alac
6996#   ac-3         36      dac3
6997#
6998# (* child atoms found at different offsets in mp4a)
6999#
7000    pinf => {
7001        Name => 'PurchaseInfo',
7002        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ProtectionInfo' },
7003    },
7004    sinf => { # "protection scheme information"
7005        Name => 'ProtectionInfo', #3
7006        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ProtectionInfo' },
7007    },
7008    # f - 16/36 bytes
7009    # esds - 31/40/42/43 bytes - ES descriptor (ref 3)
7010    damr => { #3
7011        Name => 'DecodeConfig',
7012        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::DecodeConfig' },
7013    },
7014    wave => {
7015        Name => 'Wave',
7016        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Wave' },
7017    },
7018    chan => {
7019        Name => 'AudioChannelLayout',
7020        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ChannelLayout' },
7021    }
7022    # alac - 28 bytes
7023    # adrm - AAX DRM atom? 148 bytes
7024    # aabd - AAX unknown 17kB (contains 'aavd' strings)
7025    # SA3D - written by Garmin VIRB360
7026);
7027
7028# AMR decode config box (ref 3)
7029%Image::ExifTool::QuickTime::DecodeConfig = (
7030    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7031    GROUPS => { 2 => 'Audio' },
7032    0 => {
7033        Name => 'EncoderVendor',
7034        Format => 'undef[4]',
7035    },
7036    4 => 'EncoderVersion',
7037    # 5 - int16u - packet modes
7038    # 7 - int8u - number of packet mode changes
7039    # 8 - int8u - bytes per packet
7040);
7041
7042%Image::ExifTool::QuickTime::ProtectionInfo = (
7043    PROCESS_PROC => \&ProcessMOV,
7044    GROUPS => { 2 => 'Audio' },
7045    NOTES => 'Child atoms found in "sinf" and/or "pinf" atoms.',
7046    frma => 'OriginalFormat',
7047    # imif - IPMP information
7048    schm => {
7049        Name => 'SchemeType',
7050        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SchemeType' },
7051    },
7052    schi => {
7053        Name => 'SchemeInfo',
7054        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SchemeInfo' },
7055    },
7056    enda => {
7057        Name => 'Endianness',
7058        Format => 'int16u',
7059        PrintConv => {
7060            0 => 'Big-endian (Motorola, MM)',
7061            1 => 'Little-endian (Intel, II)',
7062        },
7063    },
7064    # skcr
7065);
7066
7067%Image::ExifTool::QuickTime::Wave = (
7068    PROCESS_PROC => \&ProcessMOV,
7069    frma => 'PurchaseFileFormat',
7070    enda => {
7071        Name => 'Endianness',
7072        Format => 'int16u',
7073        PrintConv => {
7074            0 => 'Big-endian (Motorola, MM)',
7075            1 => 'Little-endian (Intel, II)',
7076        },
7077    },
7078    # "ms\0\x11" - 20 bytes
7079);
7080
7081# audio channel layout (ref CoreAudioTypes.h)
7082%Image::ExifTool::QuickTime::ChannelLayout = (
7083    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7084    GROUPS => { 2 => 'Audio' },
7085    DATAMEMBER => [ 0, 8 ],
7086    NOTES => 'Audio channel layout.',
7087    # 0 - version and flags
7088    4 => {
7089        Name => 'LayoutFlags',
7090        Format => 'int16u',
7091        RawConv => '$$self{LayoutFlags} = $val',
7092        PrintConvColumns => 2,
7093        PrintConv => {
7094            0 => 'UseDescriptions',
7095            1 => 'UseBitmap',
7096            100 => 'Mono',
7097            101 => 'Stereo',
7098            102 => 'StereoHeadphones',
7099            100 => 'Mono',
7100            101 => 'Stereo',
7101            102 => 'StereoHeadphones',
7102            103 => 'MatrixStereo',
7103            104 => 'MidSide',
7104            105 => 'XY',
7105            106 => 'Binaural',
7106            107 => 'Ambisonic_B_Format',
7107            108 => 'Quadraphonic',
7108            109 => 'Pentagonal',
7109            110 => 'Hexagonal',
7110            111 => 'Octagonal',
7111            112 => 'Cube',
7112            113 => 'MPEG_3_0_A',
7113            114 => 'MPEG_3_0_B',
7114            115 => 'MPEG_4_0_A',
7115            116 => 'MPEG_4_0_B',
7116            117 => 'MPEG_5_0_A',
7117            118 => 'MPEG_5_0_B',
7118            119 => 'MPEG_5_0_C',
7119            120 => 'MPEG_5_0_D',
7120            121 => 'MPEG_5_1_A',
7121            122 => 'MPEG_5_1_B',
7122            123 => 'MPEG_5_1_C',
7123            124 => 'MPEG_5_1_D',
7124            125 => 'MPEG_6_1_A',
7125            126 => 'MPEG_7_1_A',
7126            127 => 'MPEG_7_1_B',
7127            128 => 'MPEG_7_1_C',
7128            129 => 'Emagic_Default_7_1',
7129            130 => 'SMPTE_DTV',
7130            131 => 'ITU_2_1',
7131            132 => 'ITU_2_2',
7132            133 => 'DVD_4',
7133            134 => 'DVD_5',
7134            135 => 'DVD_6',
7135            136 => 'DVD_10',
7136            137 => 'DVD_11',
7137            138 => 'DVD_18',
7138            139 => 'AudioUnit_6_0',
7139            140 => 'AudioUnit_7_0',
7140            141 => 'AAC_6_0',
7141            142 => 'AAC_6_1',
7142            143 => 'AAC_7_0',
7143            144 => 'AAC_Octagonal',
7144            145 => 'TMH_10_2_std',
7145            146 => 'TMH_10_2_full',
7146            147 => 'DiscreteInOrder',
7147            148 => 'AudioUnit_7_0_Front',
7148            149 => 'AC3_1_0_1',
7149            150 => 'AC3_3_0',
7150            151 => 'AC3_3_1',
7151            152 => 'AC3_3_0_1',
7152            153 => 'AC3_2_1_1',
7153            154 => 'AC3_3_1_1',
7154            155 => 'EAC_6_0_A',
7155            156 => 'EAC_7_0_A',
7156            157 => 'EAC3_6_1_A',
7157            158 => 'EAC3_6_1_B',
7158            159 => 'EAC3_6_1_C',
7159            160 => 'EAC3_7_1_A',
7160            161 => 'EAC3_7_1_B',
7161            162 => 'EAC3_7_1_C',
7162            163 => 'EAC3_7_1_D',
7163            164 => 'EAC3_7_1_E',
7164            165 => 'EAC3_7_1_F',
7165            166 => 'EAC3_7_1_G',
7166            167 => 'EAC3_7_1_H',
7167            168 => 'DTS_3_1',
7168            169 => 'DTS_4_1',
7169            170 => 'DTS_6_0_A',
7170            171 => 'DTS_6_0_B',
7171            172 => 'DTS_6_0_C',
7172            173 => 'DTS_6_1_A',
7173            174 => 'DTS_6_1_B',
7174            175 => 'DTS_6_1_C',
7175            176 => 'DTS_7_0',
7176            177 => 'DTS_7_1',
7177            178 => 'DTS_8_0_A',
7178            179 => 'DTS_8_0_B',
7179            180 => 'DTS_8_1_A',
7180            181 => 'DTS_8_1_B',
7181            182 => 'DTS_6_1_D',
7182            183 => 'AAC_7_1_B',
7183            0xffff => 'Unknown',
7184        },
7185    },
7186    6  => {
7187        Name => 'AudioChannels',
7188        Condition => '$$self{LayoutFlags} != 0 and $$self{LayoutFlags} != 1',
7189        Format => 'int16u',
7190    },
7191    8 => {
7192        Name => 'AudioChannelTypes',
7193        Condition => '$$self{LayoutFlags} == 1',
7194        Format => 'int32u',
7195        PrintConv => { BITMASK => {
7196            0 => 'Left',
7197            1 => 'Right',
7198            2 => 'Center',
7199            3 => 'LFEScreen',
7200            4 => 'LeftSurround',
7201            5 => 'RightSurround',
7202            6 => 'LeftCenter',
7203            7 => 'RightCenter',
7204            8 => 'CenterSurround',
7205            9 => 'LeftSurroundDirect',
7206            10 => 'RightSurroundDirect',
7207            11 => 'TopCenterSurround',
7208            12 => 'VerticalHeightLeft',
7209            13 => 'VerticalHeightCenter',
7210            14 => 'VerticalHeightRight',
7211            15 => 'TopBackLeft',
7212            16 => 'TopBackCenter',
7213            17 => 'TopBackRight',
7214        }},
7215    },
7216    12  => {
7217        Name => 'NumChannelDescriptions',
7218        Condition => '$$self{LayoutFlags} == 1',
7219        Format => 'int32u',
7220        RawConv => '$$self{NumChannelDescriptions} = $val',
7221    },
7222    16 => {
7223        Name => 'Channel1Label',
7224        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 0',
7225        Format => 'int32u',
7226        SeparateTable => 'ChannelLabel',
7227        PrintConv => \%channelLabel,
7228    },
7229    20 => {
7230        Name => 'Channel1Flags',
7231        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 0',
7232        Format => 'int32u',
7233        PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7234    },
7235    24 => {
7236        Name => 'Channel1Coordinates',
7237        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 0',
7238        Notes => q{
7239            3 numbers:  for rectangular coordinates left/right, back/front, down/up; for
7240            spherical coordinates left/right degrees, down/up degrees, distance
7241        },
7242        Format => 'float[3]',
7243    },
7244    36 => {
7245        Name => 'Channel2Label',
7246        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 1',
7247        Format => 'int32u',
7248        SeparateTable => 'ChannelLabel',
7249        PrintConv => \%channelLabel,
7250    },
7251    40 => {
7252        Name => 'Channel2Flags',
7253        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 1',
7254        Format => 'int32u',
7255        PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7256    },
7257    44 => {
7258        Name => 'Channel2Coordinates',
7259        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 1',
7260        Format => 'float[3]',
7261    },
7262    56 => {
7263        Name => 'Channel3Label',
7264        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 2',
7265        Format => 'int32u',
7266        SeparateTable => 'ChannelLabel',
7267        PrintConv => \%channelLabel,
7268    },
7269    60 => {
7270        Name => 'Channel3Flags',
7271        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 2',
7272        Format => 'int32u',
7273        PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7274    },
7275    64 => {
7276        Name => 'Channel3Coordinates',
7277        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 2',
7278        Format => 'float[3]',
7279    },
7280    76 => {
7281        Name => 'Channel4Label',
7282        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 3',
7283        Format => 'int32u',
7284        SeparateTable => 'ChannelLabel',
7285        PrintConv => \%channelLabel,
7286    },
7287    80 => {
7288        Name => 'Channel4Flags',
7289        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 3',
7290        Format => 'int32u',
7291        PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7292    },
7293    84 => {
7294        Name => 'Channel4Coordinates',
7295        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 3',
7296        Format => 'float[3]',
7297    },
7298    96 => {
7299        Name => 'Channel5Label',
7300        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 4',
7301        Format => 'int32u',
7302        SeparateTable => 'ChannelLabel',
7303        PrintConv => \%channelLabel,
7304    },
7305    100 => {
7306        Name => 'Channel5Flags',
7307        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 4',
7308        Format => 'int32u',
7309        PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7310    },
7311    104 => {
7312        Name => 'Channel5Coordinates',
7313        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 4',
7314        Format => 'float[3]',
7315    },
7316    116 => {
7317        Name => 'Channel6Label',
7318        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 5',
7319        Format => 'int32u',
7320        SeparateTable => 'ChannelLabel',
7321        PrintConv => \%channelLabel,
7322    },
7323    120 => {
7324        Name => 'Channel6Flags',
7325        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 5',
7326        Format => 'int32u',
7327        PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7328    },
7329    124 => {
7330        Name => 'Channel6Coordinates',
7331        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 5',
7332        Format => 'float[3]',
7333    },
7334    136 => {
7335        Name => 'Channel7Label',
7336        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 6',
7337        Format => 'int32u',
7338        SeparateTable => 'ChannelLabel',
7339        PrintConv => \%channelLabel,
7340    },
7341    140 => {
7342        Name => 'Channel7Flags',
7343        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 6',
7344        Format => 'int32u',
7345        PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7346    },
7347    144 => {
7348        Name => 'Channel7Coordinates',
7349        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 6',
7350        Format => 'float[3]',
7351    },
7352    156 => {
7353        Name => 'Channel8Label',
7354        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 7',
7355        Format => 'int32u',
7356        SeparateTable => 'ChannelLabel',
7357        PrintConv => \%channelLabel,
7358    },
7359    160 => {
7360        Name => 'Channel8Flags',
7361        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 7',
7362        Format => 'int32u',
7363        PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7364    },
7365    164 => {
7366        Name => 'Channel8Coordinates',
7367        Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 7',
7368        Format => 'float[3]',
7369    },
7370    # (arbitrarily decode only first 8 channels)
7371);
7372
7373# scheme type atom
7374# ref http://xhelmboyx.tripod.com/formats/mp4-layout.txt
7375%Image::ExifTool::QuickTime::SchemeType = (
7376    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7377    GROUPS => { 2 => 'Audio' },
7378    # 0 - 4 bytes version
7379    4 => { Name => 'SchemeType',    Format => 'undef[4]' },
7380    8 => { Name => 'SchemeVersion', Format => 'int16u' },
7381    10 => { Name => 'SchemeURL',    Format => 'string[$size-10]' },
7382);
7383
7384%Image::ExifTool::QuickTime::SchemeInfo = (
7385    PROCESS_PROC => \&ProcessMOV,
7386    GROUPS => { 2 => 'Audio' },
7387    user => {
7388        Name => 'UserID',
7389        Groups => { 2 => 'Author' },
7390        ValueConv => '"0x" . unpack("H*",$val)',
7391    },
7392    cert => { # ref http://www.onvif.org/specs/stream/ONVIF-ExportFileFormat-Spec-v100.pdf
7393        Name => 'Certificate',
7394        ValueConv => '"0x" . unpack("H*",$val)',
7395    },
7396    'key ' => {
7397        Name => 'KeyID',
7398        ValueConv => '"0x" . unpack("H*",$val)',
7399    },
7400    iviv => {
7401        Name => 'InitializationVector',
7402        ValueConv => 'unpack("H*",$val)',
7403    },
7404    righ => {
7405        Name => 'Rights',
7406        Groups => { 2 => 'Author' },
7407        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Rights' },
7408    },
7409    name => { Name => 'UserName', Groups => { 2 => 'Author' } },
7410    # chtb
7411    # priv - private data
7412    # sign
7413    # adkm - Adobe DRM key management system (ref http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf)
7414    # iKMS
7415    # iSFM
7416    # iSLT
7417);
7418
7419%Image::ExifTool::QuickTime::Rights = (
7420    PROCESS_PROC => \&ProcessRights,
7421    GROUPS => { 2 => 'Audio' },
7422    veID => 'ItemVendorID', #PH ("VendorID" ref 19)
7423    plat => 'Platform', #18?
7424    aver => 'VersionRestrictions', #19 ("appversion?" ref 18)
7425    tran => 'TransactionID', #18
7426    song => 'ItemID', #19 ("appid" ref 18)
7427    tool => {
7428        Name => 'ItemTool', #PH (guess) ("itunes build?" ref 18)
7429        Format => 'string',
7430    },
7431    medi => 'MediaFlags', #PH (?)
7432    mode => 'ModeFlags', #PH (?) 0x04 is HD flag (https://compilr.com/heksesang/requiem-mac/UnDrm.java)
7433);
7434
7435# MP4 hint sample description box (ref 5)
7436# (ref https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-SW1)
7437%Image::ExifTool::QuickTime::HintSampleDesc = (
7438    PROCESS_PROC => \&ProcessHybrid,
7439    VARS => { ID_LABEL => 'ID/Index' },
7440    NOTES => 'MP4 hint sample description.',
7441    4  => { Name => 'HintFormat', Format => 'undef[4]' },
7442    # 14 - int16u DataReferenceIndex
7443    16 => { Name => 'HintTrackVersion', Format => 'int16u' },
7444    # 18 - int16u LastCompatibleHintTrackVersion
7445    20 => { Name => 'MaxPacketSize', Format => 'int32u' },
7446#
7447# Observed offsets for child atoms of various HintFormat types:
7448#
7449#   HintFormat   Offset  Child atoms
7450#   -----------  ------  ----------------
7451#   "rtp "       24      tims
7452#
7453    tims => { Name => 'RTPTimeScale',               Format => 'int32u' },
7454    tsro => { Name => 'TimestampRandomOffset',      Format => 'int32u' },
7455    snro => { Name => 'SequenceNumberRandomOffset', Format => 'int32u' },
7456);
7457
7458# MP4 metadata sample description box
7459%Image::ExifTool::QuickTime::MetaSampleDesc = (
7460    PROCESS_PROC => \&ProcessHybrid,
7461    NOTES => 'MP4 metadata sample description.',
7462    4 => {
7463        Name => 'MetaFormat',
7464        Format => 'undef[4]',
7465        RawConv => '$$self{MetaFormat} = $val',
7466    },
7467#
7468# Observed offsets for child atoms of various MetaFormat types:
7469#
7470#   MetaFormat   Offset  Child atoms
7471#   -----------  ------  ----------------
7472#   mebx         24      keys,btrt,lidp,lidl
7473#   fdsc         -       -
7474#   gpmd         -       -
7475#   rtmd         -       -
7476#   CTMD         -       -
7477#
7478   'keys' => { #PH (iPhone7+ hevc)
7479        Name => 'Keys',
7480        SubDirectory => {
7481            TagTable => 'Image::ExifTool::QuickTime::Keys',
7482            ProcessProc => \&ProcessMetaKeys,
7483        },
7484    },
7485    btrt => {
7486        Name => 'BitrateInfo',
7487        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Bitrate' },
7488    },
7489);
7490
7491# MP4 generic sample description box
7492%Image::ExifTool::QuickTime::OtherSampleDesc = (
7493    PROCESS_PROC => \&ProcessHybrid,
7494    4 => {
7495        Name => 'OtherFormat',
7496        Format => 'undef[4]',
7497        RawConv => '$$self{MetaFormat} = $val', # (yes, use MetaFormat for this too)
7498    },
7499#
7500# Observed offsets for child atoms of various OtherFormat types:
7501#
7502#   OtherFormat  Offset  Child atoms
7503#   -----------  ------  ----------------
7504#   avc1         86      avcC
7505#   mp4a         36      esds
7506#   mp4s         16      esds
7507#   tmcd         34      name
7508#   data         -       -
7509#
7510    ftab => { Name => 'FontTable',  Format => 'undef', ValueConv => 'substr($val, 5)' },
7511    name => { Name => 'OtherName',  Format => 'undef', ValueConv => 'substr($val, 4)' },
7512    # mrlh = GM header?
7513    # mrlv = GM data
7514    # mrld = GM data (448-byte records):
7515    #            0 - int32u count
7516    #            4 - int32u ? (related to units) 0=none,1=m/km,2=L,3=kph,4=C,7=deg,8=rpm,9=kPa,10=G,11=V,15=Nm,16=%
7517    #            8 - int32u ? (0,1,3,4,5)
7518    #           12 - string[64] units
7519    #           76 - int32u ? (1,3,7,15)
7520    #           80 - int32u 0
7521    #           84 - undef[4] ?
7522    #           88 - int16u[6] ?
7523    #           100 - undef[32] ?
7524    #           132 - string[64] measurement name
7525    #           196 - string[64] measurement name
7526);
7527
7528# MP4 data information box (ref 5)
7529%Image::ExifTool::QuickTime::DataInfo = (
7530    PROCESS_PROC => \&ProcessMOV,
7531    WRITE_PROC => \&WriteQuickTime, # (necessary to parse dref even though we don't change it)
7532    NOTES => 'MP4 data information box.',
7533    dref => {
7534        Name => 'DataRef',
7535        SubDirectory => {
7536            TagTable => 'Image::ExifTool::QuickTime::DataRef',
7537            Start => 8,
7538        },
7539    },
7540);
7541
7542# Generic media header
7543%Image::ExifTool::QuickTime::GenMediaHeader = (
7544    PROCESS_PROC => \&ProcessMOV,
7545    gmin => {
7546        Name => 'GenMediaInfo',
7547        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::GenMediaInfo' },
7548    },
7549    text => {
7550        Name => 'Text',
7551        Flags => ['Binary','Unknown'],
7552    },
7553    tmcd => {
7554        Name => 'TimeCode',
7555        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TimeCode' },
7556    },
7557);
7558
7559# TimeCode header
7560%Image::ExifTool::QuickTime::TimeCode = (
7561    PROCESS_PROC => \&ProcessMOV,
7562    tcmi => {
7563        Name => 'TCMediaInfo',
7564        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TCMediaInfo' },
7565    },
7566);
7567
7568# TimeCode media info (ref 12)
7569%Image::ExifTool::QuickTime::TCMediaInfo = (
7570    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7571    GROUPS => { 2 => 'Video' },
7572    4 => {
7573        Name => 'TextFont',
7574        Format => 'int16u',
7575        PrintConv => { 0 => 'System' },
7576    },
7577    6 => {
7578        Name => 'TextFace',
7579        Format => 'int16u',
7580        PrintConv => {
7581            0 => 'Plain',
7582            BITMASK => {
7583                0 => 'Bold',
7584                1 => 'Italic',
7585                2 => 'Underline',
7586                3 => 'Outline',
7587                4 => 'Shadow',
7588                5 => 'Condense',
7589                6 => 'Extend',
7590            },
7591        },
7592    },
7593    8 => {
7594        Name => 'TextSize',
7595        Format => 'int16u',
7596    },
7597    # 10 - reserved
7598    12 => {
7599        Name => 'TextColor',
7600        Format => 'int16u[3]',
7601    },
7602    18 => {
7603        Name => 'BackgroundColor',
7604        Format => 'int16u[3]',
7605    },
7606    24 => {
7607        Name => 'FontName',
7608        Format => 'pstring',
7609        ValueConv => '$self->Decode($val, $self->Options("CharsetQuickTime"))',
7610    },
7611);
7612
7613# Generic media info (ref http://sourceforge.jp/cvs/view/ntvrec/ntvrec/libqtime/gmin.h?view=co)
7614%Image::ExifTool::QuickTime::GenMediaInfo = (
7615    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7616    GROUPS => { 2 => 'Video' },
7617    0  => 'GenMediaVersion',
7618    1  => { Name => 'GenFlags',   Format => 'int8u[3]' },
7619    4  => { Name => 'GenGraphicsMode',
7620        Format => 'int16u',
7621        PrintHex => 1,
7622        SeparateTable => 'GraphicsMode',
7623        PrintConv => \%graphicsMode,
7624    },
7625    6  => { Name => 'GenOpColor', Format => 'int16u[3]' },
7626    12 => { Name => 'GenBalance', Format => 'fixed16s' },
7627);
7628
7629# MP4 data reference box (ref 5)
7630%Image::ExifTool::QuickTime::DataRef = (
7631    PROCESS_PROC => \&ProcessMOV,
7632    WRITE_PROC => \&WriteQuickTime, # (necessary to parse dref even though we don't change it)
7633    NOTES => 'MP4 data reference box.',
7634    'url ' => {
7635        Name => 'URL',
7636        Format => 'undef',  # (necessary to prevent decoding as string!)
7637        RawConv => q{
7638            # ignore if self-contained (flags bit 0 set)
7639            return undef if unpack("N",$val) & 0x01;
7640            $_ = substr($val,4); s/\0.*//s; $_;
7641        },
7642    },
7643    "url\0" => { # (written by GoPro)
7644        Name => 'URL',
7645        Format => 'undef',  # (necessary to prevent decoding as string!)
7646        RawConv => q{
7647            # ignore if self-contained (flags bit 0 set)
7648            return undef if unpack("N",$val) & 0x01;
7649            $_ = substr($val,4); s/\0.*//s; $_;
7650        },
7651    },
7652    'urn ' => {
7653        Name => 'URN',
7654        Format => 'undef',  # (necessary to prevent decoding as string!)
7655        RawConv => q{
7656            return undef if unpack("N",$val) & 0x01;
7657            $_ = substr($val,4); s/\0+/; /; s/\0.*//s; $_;
7658        },
7659    },
7660);
7661
7662# MP4 handler box (ref 5)
7663%Image::ExifTool::QuickTime::Handler = (
7664    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7665    GROUPS => { 2 => 'Video' },
7666    4 => { #PH
7667        Name => 'HandlerClass',
7668        Format => 'undef[4]',
7669        RawConv => '$val eq "\0\0\0\0" ? undef : $val',
7670        PrintConv => {
7671            mhlr => 'Media Handler',
7672            dhlr => 'Data Handler',
7673        },
7674    },
7675    8 => {
7676        Name => 'HandlerType',
7677        Format => 'undef[4]',
7678        RawConv => q{
7679            $$self{HandlerType} = $val unless $val eq 'alis' or $val eq 'url ';
7680            $$self{HasHandler}{$val} = 1; # remember all our handlers
7681            return $val;
7682        },
7683        PrintConvColumns => 2,
7684        PrintConv => {
7685            alis => 'Alias Data', #PH
7686            crsm => 'Clock Reference', #3
7687            hint => 'Hint Track',
7688            ipsm => 'IPMP', #3
7689            m7sm => 'MPEG-7 Stream', #3
7690            meta => 'NRT Metadata', #PH
7691            mdir => 'Metadata', #3
7692            mdta => 'Metadata Tags', #PH
7693            mjsm => 'MPEG-J', #3
7694            ocsm => 'Object Content', #3
7695            odsm => 'Object Descriptor', #3
7696            priv => 'Private', #PH
7697            sdsm => 'Scene Description', #3
7698            soun => 'Audio Track',
7699            text => 'Text', #PH (but what type? subtitle?)
7700            tmcd => 'Time Code', #PH
7701           'url '=> 'URL', #3
7702            vide => 'Video Track',
7703            subp => 'Subpicture', #http://www.google.nl/patents/US7778526
7704            nrtm => 'Non-Real Time Metadata', #PH (Sony ILCE-7S) [how is this different from "meta"?]
7705            pict => 'Picture', # (HEIC images)
7706            camm => 'Camera Metadata', # (Insta360 MP4)
7707            psmd => 'Panasonic Static Metadata', #PH (Leica C-Lux CAM-DC25)
7708            data => 'Data', #PH (GPS and G-sensor data from DataKam)
7709            sbtl => 'Subtitle', #PH (TomTom Bandit Action Cam)
7710        },
7711    },
7712    12 => { #PH
7713        Name => 'HandlerVendorID',
7714        Format => 'undef[4]',
7715        RawConv => '$val eq "\0\0\0\0" ? undef : $val',
7716        PrintConv => \%vendorID,
7717        SeparateTable => 'VendorID',
7718    },
7719    24 => {
7720        Name => 'HandlerDescription',
7721        Format => 'string',
7722        # (sometimes this is a Pascal string, and sometimes it is a C string)
7723        RawConv => q{
7724            $val=substr($val,1,ord($1)) if $val=~/^([\0-\x1f])/ and ord($1)<length($val);
7725            length $val ? $val : undef;
7726        },
7727    },
7728);
7729
7730# Flip uuid data (ref PH)
7731%Image::ExifTool::QuickTime::Flip = (
7732    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7733    FORMAT => 'int32u',
7734    FIRST_ENTRY => 0,
7735    NOTES => 'Found in MP4 files from Flip Video cameras.',
7736    GROUPS => { 1 => 'MakerNotes', 2 => 'Image' },
7737    1 => 'PreviewImageWidth',
7738    2 => 'PreviewImageHeight',
7739    13 => 'PreviewImageLength',
7740    14 => { # (confirmed for FlipVideoMinoHD)
7741        Name => 'SerialNumber',
7742        Groups => { 2 => 'Camera' },
7743        Format => 'string[16]',
7744    },
7745    28 => {
7746        Name => 'PreviewImage',
7747        Groups => { 2 => 'Preview' },
7748        Format => 'undef[$val{13}]',
7749        RawConv => '$self->ValidateImage(\$val, $tag)',
7750    },
7751);
7752
7753# atoms in Pittasoft "free" atom
7754%Image::ExifTool::QuickTime::Pittasoft = (
7755    PROCESS_PROC => \&ProcessMOV,
7756    NOTES => 'Tags found in Pittasoft Blackvue dashcam "free" data.',
7757    cprt => 'Copyright',
7758    thum => {
7759        Name => 'PreviewImage',
7760        Groups => { 2 => 'Preview' },
7761        Binary => 1,
7762        RawConv => q{
7763            return undef unless length $val > 4;
7764            my $len = unpack('N', $val);
7765            return undef unless length $val >= 4 + $len;
7766            return substr($val, 4, $len);
7767        },
7768    },
7769    ptnm => {
7770        Name => 'OriginalFileName',
7771        ValueConv => 'substr($val, 4, -1)',
7772    },
7773    ptrh => {
7774        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Pittasoft' },
7775        # contains these atoms:
7776        # ptvi - 27 bytes: '..avc1...'
7777        # ptso - 16 bytes: '..mp4a...'
7778    },
7779    'gps ' => {
7780        Name => 'GPSLog',
7781        Binary => 1,    # (ASCII NMEA track log with leading timestamps)
7782        Notes => 'parsed to extract GPS separately when ExtractEmbedded is used',
7783        RawConv => q{
7784            $val =~ s/\0+$//;   # remove trailing nulls
7785            if (length $val and $$self{OPTIONS}{ExtractEmbedded}) {
7786                my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
7787                Image::ExifTool::QuickTime::ProcessGPSLog($self, { DataPt => \$val }, $tagTbl);
7788            }
7789            return $val;
7790        },
7791    },
7792    '3gf ' => {
7793        Name => 'AccelData',
7794        SubDirectory => {
7795            TagTable => 'Image::ExifTool::QuickTime::Stream',
7796            ProcessProc => \&Process_3gf,
7797        },
7798    },
7799    sttm => {
7800        Name => 'StartTime',
7801        Format => 'int64u',
7802        Groups => { 2 => 'Time' },
7803        RawConv => '$$self{StartTime} = $val',
7804        # (ms since Jan 1, 1970, in local time zone - PH)
7805        ValueConv => q{
7806            my $secs = int($val / 1000);
7807            return ConvertUnixTime($secs) . sprintf(".%03d",$val - $secs * 1000);
7808        },
7809        PrintConv => '$self->ConvertDateTime($val)',
7810    },
7811);
7812
7813# QuickTime composite tags
7814%Image::ExifTool::QuickTime::Composite = (
7815    GROUPS => { 2 => 'Video' },
7816    Rotation => {
7817        Notes => q{
7818            writing this tag updates QuickTime MatrixStructure for all tracks with a
7819            non-zero image size
7820        },
7821        Require => {
7822            0 => 'QuickTime:MatrixStructure',
7823            1 => 'QuickTime:HandlerType',
7824        },
7825        Writable => 1,
7826        Protected => 1,
7827        WriteAlso => {
7828            MatrixStructure => 'Image::ExifTool::QuickTime::GetRotationMatrix($val)',
7829        },
7830        ValueConv => 'Image::ExifTool::QuickTime::CalcRotation($self)',
7831        ValueConvInv => '$val',
7832    },
7833    AvgBitrate => {
7834        Priority => 0,  # let QuickTime::AvgBitrate take priority
7835        Require => {
7836            0 => 'QuickTime::MediaDataSize',
7837            1 => 'QuickTime::Duration',
7838        },
7839        RawConv => q{
7840            return undef unless $val[1];
7841            $val[1] /= $$self{TimeScale} if $$self{TimeScale};
7842            my $key = 'MediaDataSize';
7843            my $size = $val[0];
7844            for (;;) {
7845                $key = $self->NextTagKey($key) or last;
7846                $size += $self->GetValue($key, 'ValueConv');
7847            }
7848            return int($size * 8 / $val[1] + 0.5);
7849        },
7850        PrintConv => 'ConvertBitrate($val)',
7851    },
7852    GPSLatitude => {
7853        Require => 'QuickTime:GPSCoordinates',
7854        Groups => { 2 => 'Location' },
7855        ValueConv => 'my @c = split " ", $val; $c[0]',
7856        PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
7857    },
7858    GPSLongitude => {
7859        Require => 'QuickTime:GPSCoordinates',
7860        Groups => { 2 => 'Location' },
7861        ValueConv => 'my @c = split " ", $val; $c[1]',
7862        PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
7863    },
7864    # split altitude into GPSAltitude/GPSAltitudeRef like EXIF and XMP
7865    GPSAltitude => {
7866        Require => 'QuickTime:GPSCoordinates',
7867        Groups => { 2 => 'Location' },
7868        Priority => 0, # (because it may not exist)
7869        ValueConv => 'my @c = split " ", $val; defined $c[2] ? abs($c[2]) : undef',
7870        PrintConv => '"$val m"',
7871    },
7872    GPSAltitudeRef  => {
7873        Require => 'QuickTime:GPSCoordinates',
7874        Groups => { 2 => 'Location' },
7875        Priority => 0, # (because altitude information may not exist)
7876        ValueConv => 'my @c = split " ", $val; defined $c[2] ? ($c[2] < 0 ? 1 : 0) : undef',
7877        PrintConv => {
7878            0 => 'Above Sea Level',
7879            1 => 'Below Sea Level',
7880        },
7881    },
7882    GPSLatitude2 => {
7883        Name => 'GPSLatitude',
7884        Require => 'QuickTime:LocationInformation',
7885        Groups => { 2 => 'Location' },
7886        ValueConv => '$val =~ /Lat=([-+.\d]+)/; $1',
7887        PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
7888    },
7889    GPSLongitude2 => {
7890        Name => 'GPSLongitude',
7891        Require => 'QuickTime:LocationInformation',
7892        Groups => { 2 => 'Location' },
7893        ValueConv => '$val =~ /Lon=([-+.\d]+)/; $1',
7894        PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
7895    },
7896    GPSAltitude2 => {
7897        Name => 'GPSAltitude',
7898        Require => 'QuickTime:LocationInformation',
7899        Groups => { 2 => 'Location' },
7900        ValueConv => '$val =~ /Alt=([-+.\d]+)/; abs($1)',
7901        PrintConv => '"$val m"',
7902    },
7903    GPSAltitudeRef2  => {
7904        Name => 'GPSAltitudeRef',
7905        Require => 'QuickTime:LocationInformation',
7906        Groups => { 2 => 'Location' },
7907        ValueConv => '$val =~ /Alt=([-+.\d]+)/; $1 < 0 ? 1 : 0',
7908        PrintConv => {
7909            0 => 'Above Sea Level',
7910            1 => 'Below Sea Level',
7911        },
7912    },
7913    CDDBDiscPlayTime => {
7914        Require => 'CDDB1Info',
7915        Groups => { 2 => 'Audio' },
7916        ValueConv => '$val =~ /^..([a-z0-9]{4})/i ? hex($1) : undef',
7917        PrintConv => 'ConvertDuration($val)',
7918    },
7919    CDDBDiscTracks => {
7920        Require => 'CDDB1Info',
7921        Groups => { 2 => 'Audio' },
7922        ValueConv => '$val =~ /^.{6}([a-z0-9]{2})/i ? hex($1) : undef',
7923    },
7924);
7925
7926# add our composite tags
7927Image::ExifTool::AddCompositeTags('Image::ExifTool::QuickTime');
7928
7929
7930#------------------------------------------------------------------------------
7931# AutoLoad our routines when necessary
7932#
7933sub AUTOLOAD
7934{
7935    # (Note: no need to autoload routines in QuickTimeStream that use Stream table)
7936    if ($AUTOLOAD eq 'Image::ExifTool::QuickTime::Process_mebx') {
7937        require 'Image/ExifTool/QuickTimeStream.pl';
7938        no strict 'refs';
7939        return &$AUTOLOAD(@_);
7940    } else {
7941        return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
7942    }
7943}
7944
7945#------------------------------------------------------------------------------
7946# Get rotation matrix
7947# Inputs: 0) angle in degrees
7948# Returns: 9-element rotation matrix as a string (with 0 x/y offsets)
7949sub GetRotationMatrix($)
7950{
7951    my $ang = 3.1415926536 * shift() / 180;
7952    my $cos = cos $ang;
7953    my $sin = sin $ang;
7954    my $msn = -$sin;
7955    return "$cos $sin 0 $msn $cos 0 0 0 1";
7956}
7957
7958#------------------------------------------------------------------------------
7959# Get rotation angle from a matrix
7960# Inputs: 0) rotation matrix as a string
7961# Return: positive rotation angle in degrees rounded to 3 decimal points,
7962#         or undef on error
7963sub GetRotationAngle($)
7964{
7965    my $rotMatrix = shift;
7966    my @a = split ' ', $rotMatrix;
7967    return undef if $a[0]==0 and $a[1]==0;
7968    # calculate the rotation angle (assume uniform rotation)
7969    my $angle = atan2($a[1], $a[0]) * 180 / 3.14159;
7970    $angle += 360 if $angle < 0;
7971    return int($angle * 1000 + 0.5) / 1000;
7972}
7973
7974#------------------------------------------------------------------------------
7975# Calculate rotation of video track
7976# Inputs: 0) ExifTool object ref
7977# Returns: rotation angle or undef
7978sub CalcRotation($)
7979{
7980    my $et = shift;
7981    my $value = $$et{VALUE};
7982    my ($i, $track);
7983    # get the video track family 1 group (eg. "Track1");
7984    for ($i=0; ; ++$i) {
7985        my $idx = $i ? " ($i)" : '';
7986        my $tag = "HandlerType$idx";
7987        last unless $$value{$tag};
7988        next unless $$value{$tag} eq 'vide';
7989        $track = $et->GetGroup($tag, 1);
7990        last;
7991    }
7992    return undef unless $track;
7993    # get the video track matrix
7994    for ($i=0; ; ++$i) {
7995        my $idx = $i ? " ($i)" : '';
7996        my $tag = "MatrixStructure$idx";
7997        last unless $$value{$tag};
7998        next unless $et->GetGroup($tag, 1) eq $track;
7999        return GetRotationAngle($$value{$tag});
8000    }
8001    return undef;
8002}
8003
8004#------------------------------------------------------------------------------
8005# Get MatrixStructure for a given rotation angle
8006# Inputs: 0) rotation angle (deg), 1) ExifTool ref
8007# Returns: matrix structure as a string, or undef if it can't be rotated
8008# - requires ImageSizeLookahead to determine the video image size, and doesn't
8009#   rotate matrix unless image size is valid
8010sub GetMatrixStructure($$)
8011{
8012    my ($val, $et) = @_;
8013    my @a = split ' ', $val;
8014    # pass straight through if it already has an offset
8015    return $val unless $a[6] == 0 and $a[7] == 0;
8016    my @s = split ' ', $$et{ImageSizeLookahead};
8017    my ($w, $h) = @s[12,13];
8018    return undef unless $w and $h;  # don't rotate 0-sized track
8019    $_ = Image::ExifTool::QuickTime::FixWrongFormat($_) foreach $w,$h;
8020    # apply necessary offsets for the standard rotations
8021    my $angle = GetRotationAngle($val);
8022    return undef unless defined $angle;
8023    if ($angle == 90) {
8024        @a[6,7] = ($h, 0);
8025    } elsif ($angle == 180) {
8026        @a[6,7] = ($w, $h);
8027    } elsif ($angle == 270) {
8028        @a[6,7] = (0, $w);
8029    }
8030    return "@a";
8031}
8032
8033#------------------------------------------------------------------------------
8034# Determine the average sample rate from a time-to-sample table
8035# Inputs: 0) ExifTool object ref, 1) time-to-sample table data ref
8036# Returns: average sample rate (in Hz)
8037sub CalcSampleRate($$)
8038{
8039    my ($et, $valPt) = @_;
8040    my @dat = unpack('N*', $$valPt);
8041    my ($num, $dur) = (0, 0);
8042    my $i;
8043    for ($i=2; $i<@dat-1; $i+=2) {
8044        $num += $dat[$i];               # total number of samples
8045        $dur += $dat[$i] * $dat[$i+1];  # total sample duration
8046    }
8047    return undef unless $num and $dur and $$et{MediaTS};
8048    return $num * $$et{MediaTS} / $dur;
8049}
8050
8051#------------------------------------------------------------------------------
8052# Fix incorrect format for ImageWidth/Height as written by Pentax
8053sub FixWrongFormat($)
8054{
8055    my $val = shift;
8056    return undef unless $val;
8057    return $val & 0xfff00000 ? unpack('n',pack('N',$val)) : $val;
8058}
8059
8060#------------------------------------------------------------------------------
8061# Convert ISO 6709 string to standard lag/lon format
8062# Inputs: 0) ISO 6709 string (lat, lon, and optional alt)
8063# Returns: position in decimal degrees with altitude if available
8064# Notes: Wikipedia indicates altitude may be in feet -- how is this specified?
8065sub ConvertISO6709($)
8066{
8067    my $val = shift;
8068    if ($val =~ /^([-+]\d{1,2}(?:\.\d*)?)([-+]\d{1,3}(?:\.\d*)?)([-+]\d+(?:\.\d*)?)?/) {
8069        # +DD.DDD+DDD.DDD+AA.AAA
8070        $val = ($1 + 0) . ' ' . ($2 + 0);
8071        $val .= ' ' . ($3 + 0) if $3;
8072    } elsif ($val =~ /^([-+])(\d{2})(\d{2}(?:\.\d*)?)([-+])(\d{3})(\d{2}(?:\.\d*)?)([-+]\d+(?:\.\d*)?)?/) {
8073        # +DDMM.MMM+DDDMM.MMM+AA.AAA
8074        my $lat = $2 + $3 / 60;
8075        $lat = -$lat if $1 eq '-';
8076        my $lon = $5 + $6 / 60;
8077        $lon = -$lon if $4 eq '-';
8078        $val = "$lat $lon";
8079        $val .= ' ' . ($7 + 0) if $7;
8080    } elsif ($val =~ /^([-+])(\d{2})(\d{2})(\d{2}(?:\.\d*)?)([-+])(\d{3})(\d{2})(\d{2}(?:\.\d*)?)([-+]\d+(?:\.\d*)?)?/) {
8081        # +DDMMSS.SSS+DDDMMSS.SSS+AA.AAA
8082        my $lat = $2 + $3 / 60 + $4 / 3600;
8083        $lat = -$lat if $1 eq '-';
8084        my $lon = $6 + $7 / 60 + $8 / 3600;
8085        $lon = -$lon if $5 eq '-';
8086        $val = "$lat $lon";
8087        $val .= ' ' . ($9 + 0) if $9;
8088    }
8089    return $val;
8090}
8091
8092#------------------------------------------------------------------------------
8093# Convert Nero chapter list (ref ffmpeg libavformat/movenc.c)
8094# Inputs: 0) binary chpl data
8095# Returns: chapter list
8096sub ConvertChapterList($)
8097{
8098    my $val = shift;
8099    my $size = length $val;
8100    return '<invalid>' if $size < 9;
8101    my $num = Get8u(\$val, 8);
8102    my ($i, @chapters);
8103    my $pos = 9;
8104    for ($i=0; $i<$num; ++$i) {
8105        last if $pos + 9 > $size;
8106        my $dur = Get64u(\$val, $pos) / 10000000;
8107        my $len = Get8u(\$val, $pos + 8);
8108        last if $pos + 9 + $len > $size;
8109        my $title = substr($val, $pos + 9, $len);
8110        $pos += 9 + $len;
8111        push @chapters, "$dur $title";
8112    }
8113    return \@chapters;  # return as a list
8114}
8115
8116#------------------------------------------------------------------------------
8117# Print conversion for a Nero chapter list item
8118# Inputs: 0) ValueConv chapter string
8119# Returns: formatted chapter string
8120sub PrintChapter($)
8121{
8122    my $val = shift;
8123    $val =~ /^(\S+) (.*)/ or return $val;
8124    my ($dur, $title) = ($1, $2);
8125    my $h = int($dur / 3600);
8126    $dur -= $h * 3600;
8127    my $m = int($dur / 60);
8128    my $s = $dur - $m * 60;
8129    my $ss = sprintf('%06.3f', $s);
8130    if ($ss >= 60) {
8131        $ss = '00.000';
8132        ++$m >= 60 and $m -= 60, ++$h;
8133    }
8134    return sprintf("[%d:%.2d:%s] %s",$h,$m,$ss,$title);
8135}
8136
8137#------------------------------------------------------------------------------
8138# Format GPSCoordinates for printing
8139# Inputs: 0) string with numerical lat, lon and optional alt, separated by spaces
8140#         1) ExifTool object reference
8141# Returns: PrintConv value
8142sub PrintGPSCoordinates($)
8143{
8144    my ($val, $et) = @_;
8145    my @v = split ' ', $val;
8146    my $prt = Image::ExifTool::GPS::ToDMS($et, $v[0], 1, "N") . ', ' .
8147              Image::ExifTool::GPS::ToDMS($et, $v[1], 1, "E");
8148    if (defined $v[2]) {
8149        $prt .= ', ' . ($v[2] < 0 ? -$v[2] . ' m Below' : $v[2] . ' m Above') . ' Sea Level';
8150    }
8151    return $prt;
8152}
8153
8154#------------------------------------------------------------------------------
8155# Unpack packed ISO 639/T language code
8156# Inputs: 0) packed language code (or undef/0), 1) true to not treat 'und' and 'eng' as default
8157# Returns: language code, or undef/0 for default language, or 'err' for format error
8158sub UnpackLang($;$)
8159{
8160    my ($lang, $noDef) = @_;
8161    if ($lang) {
8162        # language code is packed in 5-bit characters
8163        $lang = pack 'C*', map { (($lang>>$_)&0x1f)+0x60 } 10, 5, 0;
8164        # validate language code
8165        if ($lang =~ /^[a-z]+$/) {
8166            # treat 'eng' or 'und' as the default language
8167            undef $lang if ($lang eq 'und' or $lang eq 'eng') and not $noDef;
8168        } else {
8169            $lang = 'err';  # invalid language code
8170        }
8171    }
8172    return $lang;
8173}
8174
8175#------------------------------------------------------------------------------
8176# Get language code string given QuickTime language and country codes
8177# Inputs: 0) numerical language code, 1) numerical country code, 2) no defaults
8178# Returns: language code string (ie. "fra-FR") or undef for default language
8179sub GetLangCode($;$$)
8180{
8181    my ($lang, $ctry, $noDef) = @_;
8182    # ignore country ('ctry') and language lists ('lang') for now
8183    undef $ctry if $ctry and $ctry <= 255;
8184    undef $lang if $lang and $lang <= 255;
8185    $lang = UnpackLang($lang, $noDef);
8186    # add country code if specified
8187    if ($ctry) {
8188        $ctry = unpack('a2',pack('n',$ctry)); # unpack as ISO 3166-1
8189        # treat 'ZZ' like a default country (see ref 12)
8190        undef $ctry if $ctry eq 'ZZ';
8191        if ($ctry and $ctry =~ /^[A-Z]{2}$/) {
8192            $lang or $lang = 'und';
8193            $lang .= "-$ctry";
8194        }
8195    }
8196    return $lang;
8197}
8198
8199#------------------------------------------------------------------------------
8200# Get langInfo hash and save details about alt-lang tags
8201# Inputs: 0) ExifTool ref, 1) tagInfo hash ref, 2) locale code
8202# Returns: new tagInfo hash ref, or undef if invalid
8203sub GetLangInfoQT($$$)
8204{
8205    my ($et, $tagInfo, $langCode) = @_;
8206    my $langInfo = Image::ExifTool::GetLangInfo($tagInfo, $langCode);
8207    if ($langInfo) {
8208        $$et{QTLang} or $$et{QTLang} = [ ];
8209        push @{$$et{QTLang}}, $$langInfo{Name};
8210    }
8211    return $langInfo;
8212}
8213
8214#------------------------------------------------------------------------------
8215# Get variable-length integer from data (used by ParseItemLocation)
8216# Inputs: 0) data ref, 1) start position, 2) integer size in bytes (0, 4 or 8),
8217#         3) default value
8218# Returns: integer value, and updates current position
8219sub GetVarInt($$$;$)
8220{
8221    my ($dataPt, $pos, $n, $default) = @_;
8222    my $len = length $$dataPt;
8223    $_[1] = $pos + $n;  # update current position
8224    return undef if $pos + $n > $len;
8225    if ($n == 0) {
8226        return $default || 0;
8227    } elsif ($n == 4) {
8228        return Get32u($dataPt, $pos);
8229    } elsif ($n == 8) {
8230        return Get64u($dataPt, $pos);
8231    }
8232    return undef;
8233}
8234
8235#------------------------------------------------------------------------------
8236# Get null-terminated string from binary data (used by ParseItemInfoEntry)
8237# Inputs: 0) data ref, 1) start position
8238# Returns: string, and updates current position
8239sub GetString($$)
8240{
8241    my ($dataPt, $pos) = @_;
8242    my $len = length $$dataPt;
8243    my $str = '';
8244    while ($pos < $len) {
8245        my $ch = substr($$dataPt, $pos, 1);
8246        ++$pos;
8247        last if ord($ch) == 0;
8248        $str .= $ch;
8249    }
8250    $_[1] = $pos;   # update current position
8251    return $str;
8252}
8253
8254#------------------------------------------------------------------------------
8255# Get a printable version of the tag ID
8256# Inputs: 0) tag ID, 1) Flag: 0x01 - print as 4- or 8-digit hex value if necessary
8257#                             0x02 - put leading backslash before escaped character
8258# Returns: Printable tag ID
8259sub PrintableTagID($;$)
8260{
8261    my $tag = $_[0];
8262    my $n = ($tag =~ s/([\x00-\x1f\x7f-\xff])/'x'.unpack('H*',$1)/eg);
8263    if ($n and $_[1]) {
8264        if ($n > 2 and $_[1] & 0x01) {
8265            $tag = '0x' . unpack('H8', $_[0]);
8266            $tag =~ s/^0x0000/0x/;
8267        } elsif ($_[1] & 0x02) {
8268            ($tag = $_[0]) =~ s/([\x00-\x1f\x7f-\xff])/'\\x'.unpack('H*',$1)/eg;
8269        }
8270    }
8271    return $tag;
8272}
8273
8274#==============================================================================
8275# The following ParseXxx routines parse various boxes to extract this
8276# information about embedded items in a $$et{ItemInfo} hash, keyed by item ID:
8277#
8278# iloc:
8279#  ConstructionMethod - offset type: 0=file, 1=idat, 2=item
8280#  DataReferenceIndex - 0 for "this file", otherwise index in dref box
8281#  BaseOffset         - base for file offsets
8282#  Extents            - list of details for data in file:
8283#                           0) index  (extent_index)
8284#                           1) offset (extent_offset)
8285#                           2) length (extent_length)
8286#                           3) nlen   (length_size)
8287#                           4) lenPt  (pointer to length word)
8288# infe:
8289#  ProtectionIndex    - index if item is protected (0 for unprotected)
8290#  Name               - item name
8291#  ContentType        - mime type of item
8292#  ContentEncoding    - item encoding
8293#  URI                - URI of a 'uri '-type item
8294# ipma:
8295#  Association        - list of associated properties in the ipco container
8296#  Essential          - list of "essential" flags for the associated properties
8297# cdsc:
8298#  RefersTo           - hash lookup of flags based on referred item ID
8299# other:
8300#  DocNum             - exiftool document number for this item
8301#
8302#------------------------------------------------------------------------------
8303# Parse item location (iloc) box (ref ISO 14496-12:2015 pg.79)
8304# Inputs: 0) iloc data, 1) ExifTool ref
8305# Returns: undef, and fills in ExifTool ItemInfo hash
8306# Notes: see also Handle_iloc() in WriteQuickTime.pl
8307sub ParseItemLocation($$)
8308{
8309    my ($val, $et) = @_;
8310    my ($i, $j, $num, $pos, $id);
8311    my ($extent_index, $extent_offset, $extent_length);
8312
8313    my $verbose = $$et{IsWriting} ? 0 : $et->Options('Verbose');
8314    my $items = $$et{ItemInfo} || ($$et{ItemInfo} = { });
8315    my $len = length $val;
8316    return undef if $len < 8;
8317    my $ver = Get8u(\$val, 0);
8318    my $siz = Get16u(\$val, 4);
8319    my $noff = ($siz >> 12);
8320    my $nlen = ($siz >> 8) & 0x0f;
8321    my $nbas = ($siz >> 4) & 0x0f;
8322    my $nind = $siz & 0x0f;
8323    if ($ver < 2) {
8324        $num = Get16u(\$val, 6);
8325        $pos = 8;
8326    } else {
8327        return undef if $len < 10;
8328        $num = Get32u(\$val, 6);
8329        $pos = 10;
8330    }
8331    for ($i=0; $i<$num; ++$i) {
8332        if ($ver < 2) {
8333            return undef if $pos + 2 > $len;
8334            $id = Get16u(\$val, $pos);
8335            $pos += 2;
8336        } else {
8337            return undef if $pos + 4 > $len;
8338            $id = Get32u(\$val, $pos);
8339            $pos += 4;
8340        }
8341        if ($ver == 1 or $ver == 2) {
8342            return undef if $pos + 2 > $len;
8343            $$items{$id}{ConstructionMethod} = Get16u(\$val, $pos) & 0x0f;
8344            $pos += 2;
8345        }
8346        return undef if $pos + 2 > $len;
8347        $$items{$id}{DataReferenceIndex} = Get16u(\$val, $pos);
8348        $pos += 2;
8349        $$items{$id}{BaseOffset} = GetVarInt(\$val, $pos, $nbas);
8350        return undef if $pos + 2 > $len;
8351        my $ext_num = Get16u(\$val, $pos);
8352        $pos += 2;
8353        my @extents;
8354        for ($j=0; $j<$ext_num; ++$j) {
8355            if ($ver == 1 or $ver == 2) {
8356                $extent_index = GetVarInt(\$val, $pos, $nind, 1);
8357            }
8358            $extent_offset = GetVarInt(\$val, $pos, $noff);
8359            $extent_length = GetVarInt(\$val, $pos, $nlen);
8360            return undef unless defined $extent_length;
8361            $et->VPrint(1, "$$et{INDENT}  Item $id: const_meth=",
8362                defined $$items{$id}{ConstructionMethod} ? $$items{$id}{ConstructionMethod} : '',
8363                sprintf(" base=0x%x offset=0x%x len=0x%x\n", $$items{$id}{BaseOffset},
8364                    $extent_offset, $extent_length)) if $verbose;
8365            push @extents, [ $extent_index, $extent_offset, $extent_length, $nlen, $pos-$nlen ];
8366        }
8367        # save item location information keyed on 1-based item ID:
8368        $$items{$id}{Extents} = \@extents;
8369    }
8370    return undef;
8371}
8372
8373#------------------------------------------------------------------------------
8374# Parse content describes entry (cdsc) box
8375# Inputs: 0) cdsc data, 1) ExifTool ref
8376# Returns: undef, and fills in ExifTool ItemInfo hash
8377sub ParseContentDescribes($$)
8378{
8379    my ($val, $et) = @_;
8380    my ($id, $count, @to);
8381    if ($$et{ItemRefVersion}) {
8382        return undef if length $val < 10;
8383        ($id, $count, @to) = unpack('NnN*', $val);
8384    } else {
8385        return undef if length $val < 6;
8386        ($id, $count, @to) = unpack('nnn*', $val);
8387    }
8388    if ($count > @to) {
8389        my $str = 'Missing values in ContentDescribes box';
8390        $$et{IsWriting} ? $et->Error($str) : $et->Warn($str);
8391    } elsif ($count < @to) {
8392        $et->Warn('Ignored extra values in ContentDescribes box', 1);
8393        @to = $count;
8394    }
8395    # add all referenced item ID's to a "RefersTo" lookup
8396    $$et{ItemInfo}{$id}{RefersTo}{$_} = 1 foreach @to;
8397    return undef;
8398}
8399
8400#------------------------------------------------------------------------------
8401# Parse item information entry (infe) box (ref ISO 14496-12:2015 pg.82)
8402# Inputs: 0) infe data, 1) ExifTool ref
8403# Returns: undef, and fills in ExifTool ItemInfo hash
8404sub ParseItemInfoEntry($$)
8405{
8406    my ($val, $et) = @_;
8407    my $id;
8408
8409    my $verbose = $$et{IsWriting} ? 0 : $et->Options('Verbose');
8410    my $items = $$et{ItemInfo} || ($$et{ItemInfo} = { });
8411    my $len = length $val;
8412    return undef if $len < 4;
8413    my $ver = Get8u(\$val, 0);
8414    my $pos = 4;
8415    return undef if $pos + 4 > $len;
8416    if ($ver == 0 or $ver == 1) {
8417        $id = Get16u(\$val, $pos);
8418        $$items{$id}{ProtectionIndex} = Get16u(\$val, $pos + 2);
8419        $pos += 4;
8420        $$items{$id}{Name} = GetString(\$val, $pos);
8421        $$items{$id}{ContentType} = GetString(\$val, $pos);
8422        $$items{$id}{ContentEncoding} = GetString(\$val, $pos);
8423    } else {
8424        if ($ver == 2) {
8425            $id = Get16u(\$val, $pos);
8426            $pos += 2;
8427        } elsif ($ver == 3) {
8428            $id = Get32u(\$val, $pos);
8429            $pos += 4;
8430        }
8431        return undef if $pos + 6 > $len;
8432        $$items{$id}{ProtectionIndex} = Get16u(\$val, $pos);
8433        my $type = substr($val, $pos + 2, 4);
8434        $$items{$id}{Type} = $type;
8435        $pos += 6;
8436        $$items{$id}{Name} = GetString(\$val, $pos);
8437        if ($type eq 'mime') {
8438            $$items{$id}{ContentType} = GetString(\$val, $pos);
8439            $$items{$id}{ContentEncoding} = GetString(\$val, $pos);
8440        } elsif ($type eq 'uri ') {
8441            $$items{$id}{URI} = GetString(\$val, $pos);
8442        }
8443    }
8444    $et->VPrint(1, "$$et{INDENT}  Item $id: Type=", $$items{$id}{Type} || '',
8445                   ' Name=', $$items{$id}{Name} || '',
8446                   ' ContentType=', $$items{$id}{ContentType} || '',
8447                   "\n") if $verbose > 1;
8448    return undef;
8449}
8450
8451#------------------------------------------------------------------------------
8452# Parse item property association (ipma) box (ref https://github.com/gpac/gpac/blob/master/src/isomedia/iff.c)
8453# Inputs: 0) ipma data, 1) ExifTool ref
8454# Returns: undef, and fills in ExifTool ItemInfo hash
8455sub ParseItemPropAssoc($$)
8456{
8457    my ($val, $et) = @_;
8458    my ($i, $j, $id);
8459
8460    my $verbose = $$et{IsWriting} ? 0 : $et->Options('Verbose');
8461    my $items = $$et{ItemInfo} || ($$et{ItemInfo} = { });
8462    my $len = length $val;
8463    return undef if $len < 8;
8464    my $ver = Get8u(\$val, 0);
8465    my $flg = Get32u(\$val, 0);
8466    my $num = Get32u(\$val, 4);
8467    my $pos = 8;
8468    for ($i=0; $i<$num; ++$i) {
8469        if ($ver == 0) {
8470            return undef if $pos + 3 > $len;
8471            $id = Get16u(\$val, $pos);
8472            $pos += 2;
8473        } else {
8474            return undef if $pos + 5 > $len;
8475            $id = Get32u(\$val, $pos);
8476            $pos += 4;
8477        }
8478        my $n = Get8u(\$val, $pos++);
8479        my (@association, @essential);
8480        if ($flg & 0x01) {
8481            return undef if $pos + $n * 2 > $len;
8482            for ($j=0; $j<$n; ++$j) {
8483                my $tmp = Get16u(\$val, $pos + $j * 2);
8484                push @association, $tmp & 0x7fff;
8485                push @essential, ($tmp & 0x8000) ? 1 : 0;
8486            }
8487            $pos += $n * 2;
8488        } else {
8489            return undef if $pos + $n > $len;
8490            for ($j=0; $j<$n; ++$j) {
8491                my $tmp = Get8u(\$val, $pos + $j);
8492                push @association, $tmp & 0x7f;
8493                push @essential, ($tmp & 0x80) ? 1 : 0;
8494            }
8495            $pos += $n;
8496        }
8497        $$items{$id}{Association} = \@association;
8498        $$items{$id}{Essential} = \@essential;
8499        $et->VPrint(1, "$$et{INDENT}  Item $id properties: @association\n") if $verbose > 1;
8500    }
8501    return undef;
8502}
8503
8504#------------------------------------------------------------------------------
8505# Process item information now
8506# Inputs: 0) ExifTool ref
8507sub HandleItemInfo($)
8508{
8509    my $et = shift;
8510    my $raf = $$et{RAF};
8511    my $items = $$et{ItemInfo};
8512    my $verbose = $et->Options('Verbose');
8513    my $buff;
8514
8515    # extract information from EXIF/XMP metadata items
8516    if ($items and $raf) {
8517        push @{$$et{PATH}}, 'ItemInformation';
8518        my $curPos = $raf->Tell();
8519        my $primary = $$et{PrimaryItem};
8520        my $id;
8521        $et->VerboseDir('Processing items from ItemInformation', scalar(keys %$items));
8522        foreach $id (sort { $a <=> $b } keys %$items) {
8523            my $item = $$items{$id};
8524            my $type = $$item{ContentType} || $$item{Type} || next;
8525            if ($verbose) {
8526                # add up total length of this item for the verbose output
8527                my $len = 0;
8528                if ($$item{Extents} and @{$$item{Extents}}) {
8529                    $len += $$_[2] foreach @{$$item{Extents}};
8530                }
8531                $et->VPrint(0, "$$et{INDENT}Item $id) '${type}' ($len bytes)\n");
8532            }
8533            # get ExifTool name for this item
8534            my $name = { Exif => 'EXIF', 'application/rdf+xml' => 'XMP' }->{$type} || '';
8535            my ($warn, $extent);
8536            $warn = "Can't currently decode encoded $type metadata" if $$item{ContentEncoding};
8537            $warn = "Can't currently decode protected $type metadata" if $$item{ProtectionIndex};
8538            $warn = "Can't currently extract $type with construction method $$item{ConstructionMethod}" if $$item{ConstructionMethod};
8539            $et->WarnOnce($warn) if $warn and $name;
8540            $warn = 'Not this file' if $$item{DataReferenceIndex}; # (can only extract from "this file")
8541            unless (($$item{Extents} and @{$$item{Extents}}) or $warn) {
8542                $warn = "No Extents for $type item";
8543                $et->WarnOnce($warn) if $name;
8544            }
8545            if ($warn) {
8546                $et->VPrint(0, "$$et{INDENT}    [not extracted]  ($warn)\n") if $verbose > 2;
8547                next;
8548            }
8549            my $base = $$item{BaseOffset} || 0;
8550            if ($verbose > 2) {
8551                # do verbose hex dump
8552                my $len = 0;
8553                undef $buff;
8554                my $val = '';
8555                my $maxLen = $verbose > 3 ? 2048 : 96;
8556                foreach $extent (@{$$item{Extents}}) {
8557                    my $n = $$extent[2];
8558                    my $more = $maxLen - $len;
8559                    if ($more > 0 and $n) {
8560                        $more = $n if $more > $n;
8561                        $val .= $buff if defined $buff;
8562                        $raf->Seek($$extent[1] + $base, 0) or last;
8563                        $raf->Read($buff, $more) or last;
8564                    }
8565                    $len += $n;
8566                }
8567                if (defined $buff) {
8568                    $buff = $val . $buff if length $val;
8569                    $et->VerboseDump(\$buff, DataPos => $$item{Extents}[0][1] + $base);
8570                    my $snip = $len - length $buff;
8571                    $et->VPrint(0, "$$et{INDENT}    [snip $snip bytes]\n") if $snip;
8572                }
8573            }
8574            next unless $name;
8575            # assemble the data for this item
8576            undef $buff;
8577            my $val = '';
8578            foreach $extent (@{$$item{Extents}}) {
8579                $val .= $buff if defined $buff;
8580                $raf->Seek($$extent[1] + $base, 0) or last;
8581                $raf->Read($buff, $$extent[2]) or last;
8582            }
8583            next unless defined $buff;
8584            $buff = $val . $buff if length $val;
8585            next unless length $buff;   # ignore empty directories
8586            my ($start, $subTable, $proc);
8587            my $pos = $$item{Extents}[0][1] + $base;
8588            if ($name eq 'EXIF' and length $buff >= 4) {
8589                if ($buff =~ /^(MM\0\x2a|II\x2a\0)/) {
8590                    $et->Warn('Missing Exif header');
8591                    $start = 0;
8592                } elsif ($buff =~ /^Exif\0\0/) {
8593                    # (haven't seen this yet, but it is just a matter of time
8594                    #  until someone screws it up like this)
8595                    $et->Warn('Missing Exif header size');
8596                    $start = 6;
8597                } else {
8598                    my $n = unpack('N', $buff);
8599                    $start = 4 + $n; # skip "Exif\0\0" header if it exists
8600                    if ($start > length($buff)) {
8601                        $et->Warn('Invalid EXIF header');
8602                        next;
8603                    }
8604                    if ($$et{HTML_DUMP}) {
8605                        $et->HDump($pos, 4, 'Exif header length', "Value: $n");
8606                        $et->HDump($pos+4, $start-4, 'Exif header') if $n;
8607                    }
8608                }
8609                $subTable = GetTagTable('Image::ExifTool::Exif::Main');
8610                $proc = \&Image::ExifTool::ProcessTIFF;
8611            } else {
8612                $start = 0;
8613                $subTable = GetTagTable('Image::ExifTool::XMP::Main');
8614            }
8615            my %dirInfo = (
8616                DataPt   => \$buff,
8617                DataLen  => length $buff,
8618                DirStart => $start,
8619                DirLen   => length($buff) - $start,
8620                DataPos  => $pos,
8621                Base     => $pos + $start, # (needed for HtmlDump and IsOffset tags in binary data)
8622            );
8623            # handle processing of metadata for sub-documents
8624            if (defined $primary and $$item{RefersTo} and not $$item{RefersTo}{$primary}) {
8625                # set document number if this doesn't refer to the primary document
8626                $$et{DOC_NUM} = ++$$et{DOC_COUNT};
8627                # associate this document number with the lowest item index
8628                my ($lowest) = sort { $a <=> $b } keys %{$$item{RefersTo}};
8629                $$items{$lowest}{DocNum} = $$et{DOC_NUM};
8630            }
8631            $et->ProcessDirectory(\%dirInfo, $subTable, $proc);
8632            delete $$et{DOC_NUM};
8633        }
8634        $raf->Seek($curPos, 0);     # seek back to original position
8635        pop @{$$et{PATH}};
8636    }
8637    # process the item properties now that we should know their associations and document numbers
8638    if ($$et{ItemPropertyContainer}) {
8639        my ($dirInfo, $subTable, $proc) = @{$$et{ItemPropertyContainer}};
8640        $$et{IsItemProperty} = 1;   # set item property flag
8641        $et->ProcessDirectory($dirInfo, $subTable, $proc);
8642        delete $$et{ItemPropertyContainer};
8643        delete $$et{IsItemProperty};
8644        delete $$et{DOC_NUM};
8645    }
8646    delete $$et{ItemInfo};
8647}
8648
8649#------------------------------------------------------------------------------
8650# Warn if ExtractEmbedded option isn't used
8651# Inputs: 0) ExifTool ref
8652sub EEWarn($)
8653{
8654    my $et = shift;
8655    $et->WarnOnce('The ExtractEmbedded option may find more tags in the media data',3);
8656}
8657
8658#------------------------------------------------------------------------------
8659# Get quicktime format from flags word
8660# Inputs: 0) quicktime atom flags, 1) data length
8661# Returns: ExifTool format string
8662sub QuickTimeFormat($$)
8663{
8664    my ($flags, $len) = @_;
8665    my $format;
8666    if ($flags == 0x15 or $flags == 0x16) {
8667        $format = { 1=>'int8', 2=>'int16', 4=>'int32', 8=>'int64' }->{$len};
8668        $format .= $flags == 0x15 ? 's' : 'u' if $format;
8669    } elsif ($flags == 0x17) {
8670        $format = 'float';
8671    } elsif ($flags == 0x18) {
8672        $format = 'double';
8673    } elsif ($flags == 0x00) {
8674        $format = { 1=>'int8u', 2=>'int16u' }->{$len};
8675    }
8676    return $format;
8677}
8678
8679#------------------------------------------------------------------------------
8680# Process MPEG-4 MTDT atom (ref 11)
8681# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
8682# Returns: 1 on success
8683sub ProcessMetaData($$$)
8684{
8685    my ($et, $dirInfo, $tagTablePtr) = @_;
8686    my $dataPt = $$dirInfo{DataPt};
8687    my $dirLen = length $$dataPt;
8688    my $verbose = $et->Options('Verbose');
8689    return 0 unless $dirLen >= 2;
8690    my $count = Get16u($dataPt, 0);
8691    $verbose and $et->VerboseDir('MetaData', $count);
8692    my $i;
8693    my $pos = 2;
8694    for ($i=0; $i<$count; ++$i) {
8695        last if $pos + 10 > $dirLen;
8696        my $size = Get16u($dataPt, $pos);
8697        last if $size < 10 or $size + $pos > $dirLen;
8698        my $tag  = Get32u($dataPt, $pos + 2);
8699        my $lang = Get16u($dataPt, $pos + 6);
8700        my $enc  = Get16u($dataPt, $pos + 8);
8701        my $val  = substr($$dataPt, $pos + 10, $size);
8702        my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
8703        if ($tagInfo) {
8704            # convert language code to ASCII (ignore read-only bit)
8705            $lang = UnpackLang($lang);
8706            # handle alternate languages
8707            if ($lang) {
8708                my $langInfo = GetLangInfoQT($et, $tagInfo, $lang);
8709                $tagInfo = $langInfo if $langInfo;
8710            }
8711            $verbose and $et->VerboseInfo($tag, $tagInfo,
8712                Value  => $val,
8713                DataPt => $dataPt,
8714                Start  => $pos + 10,
8715                Size   => $size - 10,
8716            );
8717            # convert from UTF-16 BE if necessary
8718            $val = $et->Decode($val, 'UCS2') if $enc == 1;
8719            if ($enc == 0 and $$tagInfo{Unknown}) {
8720                # binary data
8721                $et->FoundTag($tagInfo, \$val);
8722            } else {
8723                $et->FoundTag($tagInfo, $val);
8724            }
8725        }
8726        $pos += $size;
8727    }
8728    return 1;
8729}
8730
8731#------------------------------------------------------------------------------
8732# Process sample description table
8733# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
8734# Returns: 1 on success
8735# (ref https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25691)
8736sub ProcessSampleDesc($$$)
8737{
8738    my ($et, $dirInfo, $tagTablePtr) = @_;
8739    my $dataPt = $$dirInfo{DataPt};
8740    my $pos = $$dirInfo{DirStart} || 0;
8741    my $dirLen = $$dirInfo{DirLen} || (length($$dataPt) - $pos);
8742    return 0 if $pos + 8 > $dirLen;
8743
8744    my $num = Get32u($dataPt, 4);   # get number of sample entries in table
8745    $pos += 8;
8746    my $i;
8747    for ($i=0; $i<$num; ++$i) {     # loop through sample entries
8748        last if $pos + 8 > $dirLen;
8749        my $size = Get32u($dataPt, $pos);
8750        last if $pos + $size > $dirLen;
8751        $$dirInfo{DirStart} = $pos;
8752        $$dirInfo{DirLen} = $size;
8753        ProcessHybrid($et, $dirInfo, $tagTablePtr);
8754        $pos += $size;
8755    }
8756    return 1;
8757}
8758
8759#------------------------------------------------------------------------------
8760# Process hybrid binary data + QuickTime container (ref PH)
8761# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
8762# Returns: 1 on success
8763sub ProcessHybrid($$$)
8764{
8765    my ($et, $dirInfo, $tagTablePtr) = @_;
8766    # brute-force search for child atoms after first 8 bytes of binary data
8767    my $dataPt = $$dirInfo{DataPt};
8768    my $dirStart = $$dirInfo{DirStart} || 0;
8769    my $dirLen = $$dirInfo{DirLen} || length($$dataPt) - $dirStart;
8770    my $end = $dirStart + $dirLen;
8771    my $pos = $dirStart + 8;   # skip length/version
8772    my $try = $pos;
8773    my $childPos;
8774
8775    while ($pos <= $end - 8) {
8776        my $tag = substr($$dataPt, $try+4, 4);
8777        # look only for well-behaved tag ID's
8778        $tag =~ /[^\w ]/ and $try = ++$pos, next;
8779        my $size = Get32u($dataPt, $try);
8780        if ($size + $try == $end) {
8781            # the atom ends exactly at the end of the parent -- this must be it
8782            $childPos = $pos;
8783            $$dirInfo{DirLen} = $pos;   # the binary data ends at the first child atom
8784            last;
8785        }
8786        if ($size < 8 or $size + $try > $end - 8) {
8787            $try = ++$pos;  # fail.  try next position
8788        } else {
8789            $try += $size;  # could be another atom following this
8790        }
8791    }
8792    # process binary data
8793    $$dirInfo{MixedTags} = 1; # ignore non-integer tag ID's
8794    $et->ProcessBinaryData($dirInfo, $tagTablePtr);
8795    # process child atoms if found
8796    if ($childPos) {
8797        $$dirInfo{DirStart} = $childPos;
8798        $$dirInfo{DirLen} = $end - $childPos;
8799        ProcessMOV($et, $dirInfo, $tagTablePtr);
8800    }
8801    return 1;
8802}
8803
8804#------------------------------------------------------------------------------
8805# Process iTunes 'righ' atom (ref PH)
8806# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
8807# Returns: 1 on success
8808sub ProcessRights($$$)
8809{
8810    my ($et, $dirInfo, $tagTablePtr) = @_;
8811    my $dataPt = $$dirInfo{DataPt};
8812    my $dataPos = $$dirInfo{Base};
8813    my $dirLen = length $$dataPt;
8814    my $unknown = $$et{OPTIONS}{Unknown} || $$et{OPTIONS}{Verbose};
8815    my $pos;
8816    $et->VerboseDir('righ', $dirLen / 8);
8817    for ($pos = 0; $pos + 8 <= $dirLen; $pos += 8) {
8818        my $tag = substr($$dataPt, $pos, 4);
8819        last if $tag eq "\0\0\0\0";
8820        my $val = substr($$dataPt, $pos + 4, 4);
8821        my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
8822        unless ($tagInfo) {
8823            next unless $unknown;
8824            my $name = PrintableTagID($tag);
8825            $tagInfo = {
8826                Name => "Unknown_$name",
8827                Description => "Unknown $name",
8828                Unknown => 1,
8829            },
8830            AddTagToTable($tagTablePtr, $tag, $tagInfo);
8831        }
8832        $val = '0x' . unpack('H*', $val) unless $$tagInfo{Format};
8833        $et->HandleTag($tagTablePtr, $tag, $val,
8834            DataPt  => $dataPt,
8835            DataPos => $dataPos,
8836            Start   => $pos + 4,
8837            Size    => 4,
8838        );
8839    }
8840    return 1;
8841}
8842
8843#------------------------------------------------------------------------------
8844# Process iTunes Encoding Params (ref PH)
8845# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
8846# Returns: 1 on success
8847sub ProcessEncodingParams($$$)
8848{
8849    my ($et, $dirInfo, $tagTablePtr) = @_;
8850    my $dataPt = $$dirInfo{DataPt};
8851    my $dirLen = length $$dataPt;
8852    my $pos;
8853    $et->VerboseDir('Encoding Params', $dirLen / 8);
8854    for ($pos = 0; $pos + 8 <= $dirLen; $pos += 8) {
8855        my ($tag, $val) = unpack("x${pos}a4N", $$dataPt);
8856        $et->HandleTag($tagTablePtr, $tag, $val);
8857    }
8858    return 1;
8859}
8860
8861#------------------------------------------------------------------------------
8862# Read Meta Keys and add tags to ItemList table ('mdta' handler) (ref PH)
8863# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
8864# Returns: 1 on success
8865sub ProcessKeys($$$)
8866{
8867    local $_;
8868    my ($et, $dirInfo, $tagTablePtr) = @_;
8869    my $dataPt = $$dirInfo{DataPt};
8870    my $dirLen = length $$dataPt;
8871    my $out;
8872    if ($et->Options('Verbose')) {
8873        $et->VerboseDir('Keys');
8874        $out = $et->Options('TextOut');
8875    }
8876    my $pos = 8;
8877    my $index = 1;
8878    ++$$et{KeysCount};  # increment key count for this directory
8879    my $itemList = GetTagTable('Image::ExifTool::QuickTime::ItemList');
8880    my $userData = GetTagTable('Image::ExifTool::QuickTime::UserData');
8881    while ($pos < $dirLen - 4) {
8882        my $len = unpack("x${pos}N", $$dataPt);
8883        last if $len < 8 or $pos + $len > $dirLen;
8884        delete $$tagTablePtr{$index};
8885        my $ns  = substr($$dataPt, $pos + 4, 4);
8886        my $tag = substr($$dataPt, $pos + 8, $len - 8);
8887        $tag =~ s/\0.*//s; # truncate at null
8888        my $full = $tag;
8889        $tag =~ s/^com\.(apple\.quicktime\.)?// if $ns eq 'mdta'; # remove apple quicktime domain
8890        $tag = "Tag_$ns" unless $tag;
8891        my $short = $tag;
8892        my $tagInfo;
8893        for (;;) {
8894            $tagInfo = $et->GetTagInfo($tagTablePtr, $tag) and last;
8895            # also try ItemList and UserData tables
8896            $tagInfo = $et->GetTagInfo($itemList, $tag) and last;
8897            $tagInfo = $et->GetTagInfo($userData, $tag) and last;
8898            # (I have some samples where the tag is a reversed ItemList or UserData tag ID)
8899            if ($tag =~ /^\w{3}\xa9$/) {
8900                $tag = pack('N', unpack('V', $tag));
8901                $tagInfo = $et->GetTagInfo($itemList, $tag) and last;
8902                $tagInfo = $et->GetTagInfo($userData, $tag);
8903                last;
8904            }
8905            if ($tag eq $full) {
8906                $tag = $short;
8907                last;
8908            }
8909            $tag = $full;
8910        }
8911        my ($newInfo, $msg);
8912        if ($tagInfo) {
8913            # copy tag information into new Keys tag
8914            $newInfo = {
8915                Name      => $$tagInfo{Name},
8916                Format    => $$tagInfo{Format},
8917                ValueConv => $$tagInfo{ValueConv},
8918                ValueConvInv => $$tagInfo{ValueConvInv},
8919                PrintConv => $$tagInfo{PrintConv},
8920                PrintConvInv => $$tagInfo{PrintConvInv},
8921                Writable  => defined $$tagInfo{Writable} ? $$tagInfo{Writable} : 1,
8922                SubDirectory => $$tagInfo{SubDirectory},
8923            };
8924            my $groups = $$tagInfo{Groups};
8925            $$newInfo{Groups} = $groups ? { %$groups } : { };
8926            $$newInfo{Groups}{$_} or $$newInfo{Groups}{$_} = $$tagTablePtr{GROUPS}{$_} foreach 0..2;
8927            $$newInfo{Groups}{1} = 'Keys';
8928        } elsif ($tag =~ /^[-\w. ]+$/ or $tag =~ /\w{4}/) {
8929            # create info for tags with reasonable id's
8930            my $name = ucfirst $tag;
8931            $name =~ tr/-0-9a-zA-Z_. //dc;
8932            $name =~ s/[. ]+(.?)/\U$1/g;
8933            $name =~ s/_([a-z])/_\U$1/g;
8934            $name =~ s/([a-z])_([A-Z])/$1$2/g;
8935            $name = "Tag_$name" if length $name < 2;
8936            $newInfo = { Name => $name, Groups => { 1 => 'Keys' } };
8937            $msg = ' (Unknown)';
8938        }
8939        # substitute this tag in the ItemList table with the given index
8940        my $id = $$et{KeysCount} . '.' . $index;
8941        if (ref $$itemList{$id} eq 'HASH') {
8942            # delete other languages too if they exist
8943            my $oldInfo = $$itemList{$id};
8944            if ($$oldInfo{OtherLang}) {
8945                delete $$itemList{$_} foreach @{$$oldInfo{OtherLang}};
8946            }
8947            delete $$itemList{$id};
8948        }
8949        if ($newInfo) {
8950            $$newInfo{KeysID} = $tag;  # save original ID for use in family 7 group name
8951            AddTagToTable($itemList, $id, $newInfo);
8952            $msg or $msg = '';
8953            $out and print $out "$$et{INDENT}Added ItemList Tag $id = ($ns) $tag$msg\n";
8954        }
8955        $pos += $len;
8956        ++$index;
8957    }
8958    return 1;
8959}
8960
8961#------------------------------------------------------------------------------
8962# Process keys in MetaSampleDesc directory
8963# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
8964# Returns: 1 on success
8965sub ProcessMetaKeys($$$)
8966{
8967    my ($et, $dirInfo, $tagTablePtr) = @_;
8968    # save this information to decode timed metadata samples when ExtractEmbedded is used
8969    SaveMetaKeys($et, $dirInfo, $tagTablePtr) if $$et{OPTIONS}{ExtractEmbedded};
8970    return 1;
8971}
8972
8973#------------------------------------------------------------------------------
8974# Process a QuickTime atom
8975# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) optional tag table ref
8976# Returns: 1 on success
8977sub ProcessMOV($$;$)
8978{
8979    local $_;
8980    my ($et, $dirInfo, $tagTablePtr) = @_;
8981    my $raf = $$dirInfo{RAF};
8982    my $dataPt = $$dirInfo{DataPt};
8983    my $verbose = $et->Options('Verbose');
8984    my $validate = $$et{OPTIONS}{Validate};
8985    my $dataPos = $$dirInfo{Base} || 0;
8986    my $dirID = $$dirInfo{DirID} || '';
8987    my $charsetQuickTime = $et->Options('CharsetQuickTime');
8988    my ($buff, $tag, $size, $track, $isUserData, %triplet, $doDefaultLang, $index);
8989    my ($dirEnd, $unkOpt, %saveOptions, $atomCount);
8990
8991    my $topLevel = not $$et{InQuickTime};
8992    $$et{InQuickTime} = 1;
8993    $$et{HandlerType} = $$et{MetaFormat} = '' unless defined $$et{HandlerType};
8994
8995    unless (defined $$et{KeysCount}) {
8996        $$et{KeysCount} = 0;    # initialize ItemList key directory count
8997        $doDefaultLang = 1;     # flag to generate default language tags
8998    }
8999    # more convenient to package data as a RandomAccess file
9000    unless ($raf) {
9001        $raf = new File::RandomAccess($dataPt);
9002        $dirEnd = $dataPos + $$dirInfo{DirLen} + ($$dirInfo{DirStart} || 0) if $$dirInfo{DirLen};
9003    }
9004    # skip leading bytes if necessary
9005    if ($$dirInfo{DirStart}) {
9006        $raf->Seek($$dirInfo{DirStart}, 1) or return 0;
9007        $dataPos += $$dirInfo{DirStart};
9008    }
9009    # read size/tag name atom header
9010    $raf->Read($buff,8) == 8 or return 0;
9011    $dataPos += 8;
9012    if ($tagTablePtr) {
9013        $isUserData = ($tagTablePtr eq \%Image::ExifTool::QuickTime::UserData);
9014    } else {
9015        $tagTablePtr = GetTagTable('Image::ExifTool::QuickTime::Main');
9016    }
9017    ($size, $tag) = unpack('Na4', $buff);
9018    if ($dataPt) {
9019        $verbose and $et->VerboseDir($$dirInfo{DirName});
9020    } else {
9021        # check on file type if called with a RAF
9022        $$tagTablePtr{$tag} or return 0;
9023        if ($tag eq 'ftyp' and $size >= 12) {
9024            # read ftyp atom to see what type of file this is
9025            my $fileType;
9026            if ($raf->Read($buff, $size-8) == $size-8) {
9027                $raf->Seek(-($size-8), 1);
9028                my $type = substr($buff, 0, 4);
9029                $$et{save_ftyp} = $type;
9030                # see if we know the extension for this file type
9031                if ($ftypLookup{$type} and $ftypLookup{$type} =~ /\(\.(\w+)/) {
9032                    $fileType = $1;
9033                # check compatible brands
9034                } elsif ($buff =~ /^.{8}(.{4})+(mp41|mp42|avc1)/s) {
9035                    $fileType = 'MP4';
9036                } elsif ($buff =~ /^.{8}(.{4})+(f4v )/s) {
9037                    $fileType = 'F4V';
9038                } elsif ($buff =~ /^.{8}(.{4})+(qt  )/s) {
9039                    $fileType = 'MOV';
9040                }
9041            }
9042            $fileType or $fileType = 'MP4'; # default to MP4
9043            $et->SetFileType($fileType, $mimeLookup{$fileType} || 'video/mp4');
9044            # temporarily set ExtractEmbedded option for CRX files
9045            $saveOptions{ExtractEmbedded} = $et->Options(ExtractEmbedded => 1) if $fileType eq 'CRX';
9046        } else {
9047            $et->SetFileType();       # MOV
9048        }
9049        SetByteOrder('MM');
9050        $$et{PRIORITY_DIR} = 'XMP';   # have XMP take priority
9051    }
9052    $$raf{NoBuffer} = 1 if $et->Options('FastScan'); # disable buffering in FastScan mode
9053
9054    my $ee = $$et{OPTIONS}{ExtractEmbedded};
9055    if ($ee) {
9056        $unkOpt = $$et{OPTIONS}{Unknown};
9057        require 'Image/ExifTool/QuickTimeStream.pl';
9058    }
9059    if ($$tagTablePtr{VARS}) {
9060        $index = $$tagTablePtr{VARS}{START_INDEX};
9061        $atomCount = $$tagTablePtr{VARS}{ATOM_COUNT};
9062    }
9063    for (;;) {
9064        my ($eeTag, $ignore);
9065        last if defined $atomCount and --$atomCount < 0;
9066        if ($size < 8) {
9067            if ($size == 0) {
9068                if ($dataPt) {
9069                    # a zero size isn't legal for contained atoms, but Canon uses it to
9070                    # terminate the CNTH atom (eg. CanonEOS100D.mov), so tolerate it here
9071                    my $pos = $raf->Tell() - 4;
9072                    $raf->Seek(0,2);
9073                    my $str = $$dirInfo{DirName} . ' with ' . ($raf->Tell() - $pos) . ' bytes';
9074                    $et->VPrint(0,"$$et{INDENT}\[Terminator found in $str remaining]");
9075                } else {
9076                    my $t = PrintableTagID($tag,2);
9077                    $et->VPrint(0,"$$et{INDENT}Tag '${t}' extends to end of file");
9078                }
9079                last;
9080            }
9081            $size == 1 or $et->Warn('Invalid atom size'), last;
9082            # read extended atom size
9083            $raf->Read($buff, 8) == 8 or $et->Warn('Truncated atom header'), last;
9084            $dataPos += 8;
9085            my ($hi, $lo) = unpack('NN', $buff);
9086            if ($hi or $lo > 0x7fffffff) {
9087                if ($hi > 0x7fffffff) {
9088                    $et->Warn('Invalid atom size');
9089                    last;
9090                } elsif (not $et->Options('LargeFileSupport')) {
9091                    $et->Warn('End of processing at large atom (LargeFileSupport not enabled)');
9092                    last;
9093                }
9094            }
9095            $size = $hi * 4294967296 + $lo - 16;
9096            $size < 0 and $et->Warn('Invalid extended size'), last;
9097        } else {
9098            $size -= 8;
9099        }
9100        if ($validate) {
9101            $$et{ValidatePath} or $$et{ValidatePath} = { };
9102            my $path = join('-', @{$$et{PATH}}, $tag);
9103            $path =~ s/-Track-/-$$et{SET_GROUP1}-/ if $$et{SET_GROUP1};
9104            if ($$et{ValidatePath}{$path} and not $dupTagOK{$tag} and not $dupDirOK{$dirID}) {
9105                my $i = Get32u(\$tag,0);
9106                my $str = $i < 255 ? "index $i" : "tag '" . PrintableTagID($tag,2) . "'";
9107                $et->WarnOnce("Duplicate $str at " . join('-', @{$$et{PATH}}));
9108                $$et{ValidatePath} = { } if $path eq 'MOV-moov'; # avoid warnings for all contained dups
9109            }
9110            $$et{ValidatePath}{$path} = 1;
9111        }
9112        if ($isUserData and $$et{SET_GROUP1}) {
9113            my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
9114            # add track name to UserData tags inside tracks
9115            $tag = $$et{SET_GROUP1} . $tag;
9116            if (not $$tagTablePtr{$tag} and $tagInfo) {
9117                my %newInfo = %$tagInfo;
9118                foreach ('Name', 'Description') {
9119                    next unless $$tagInfo{$_};
9120                    $newInfo{$_} = $$et{SET_GROUP1} . $$tagInfo{$_};
9121                    $newInfo{$_} =~ s/^(Track\d+)Track/$1/; # remove duplicate "Track" in name
9122                }
9123                AddTagToTable($tagTablePtr, $tag, \%newInfo);
9124            }
9125        }
9126        # set flag to store additional information for ExtractEmbedded option
9127        my $handlerType = $$et{HandlerType};
9128        if ($eeBox{$handlerType} and $eeBox{$handlerType}{$tag}) {
9129            if ($ee) {
9130                # (there is another 'gps ' box with a track log that doesn't contain offsets)
9131                if ($tag ne 'gps ' or $eeBox{$handlerType}{$tag} eq $dirID) {
9132                    $eeTag = 1;
9133                    $$et{OPTIONS}{Unknown} = 1; # temporarily enable "Unknown" option
9134                }
9135            } elsif ($handlerType ne 'vide' and not $$et{OPTIONS}{Validate}) {
9136                EEWarn($et);
9137            }
9138        } elsif ($ee and $ee > 1 and $eeBox2{$handlerType} and $eeBox2{$handlerType}{$tag}) {
9139            $eeTag = 1;
9140            $$et{OPTIONS}{Unknown} = 1;
9141        }
9142        my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
9143
9144        $$et{OPTIONS}{Unknown} = $unkOpt if $eeTag;     # restore Unknown option
9145
9146        # allow numerical tag ID's
9147        unless ($tagInfo) {
9148            my $id = $$et{KeysCount} . '.' . unpack('N', $tag);
9149            if ($$tagTablePtr{$id}) {
9150                $tagInfo = $et->GetTagInfo($tagTablePtr, $id);
9151                $tag = $id;
9152            }
9153        }
9154        # generate tagInfo if Unknown option set
9155        if (not defined $tagInfo and ($$et{OPTIONS}{Unknown} or
9156            $verbose or $tag =~ /^\xa9/))
9157        {
9158            my $name = PrintableTagID($tag,1);
9159            if ($name =~ /^xa9(.*)/) {
9160                $tagInfo = {
9161                    Name => "UserData_$1",
9162                    Description => "User Data $1",
9163                };
9164            } else {
9165                $tagInfo = {
9166                    Name => "Unknown_$name",
9167                    Description => "Unknown $name",
9168                    %unknownInfo,
9169                };
9170            }
9171            AddTagToTable($tagTablePtr, $tag, $tagInfo);
9172        }
9173        # save required tag sizes
9174        if ($$tagTablePtr{"$tag-size"}) {
9175            $et->HandleTag($tagTablePtr, "$tag-size", $size);
9176            $et->HandleTag($tagTablePtr, "$tag-offset", $raf->Tell()) if $$tagTablePtr{"$tag-offset"};
9177        }
9178        # load values only if associated with a tag (or verbose) and not too big
9179        if ($size > 0x2000000) {    # start to get worried above 32 MB
9180            # check for RIFF trailer (written by Auto-Vox dashcam)
9181            if ($buff =~ /^(gpsa|gps0|gsen|gsea)...\0/s) { # (yet seen only gpsa as first record)
9182                $et->VPrint(0, "Found RIFF trailer");
9183                if ($et->Options('ExtractEmbedded')) {
9184                    $raf->Seek(-8, 1) or last;  # seek back to start of trailer
9185                    my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
9186                    ProcessRIFFTrailer($et, { RAF => $raf }, $tbl);
9187                } else {
9188                    EEWarn($et);
9189                }
9190                last;
9191            }
9192            $ignore = 1;
9193            if ($tagInfo and not $$tagInfo{Unknown} and not $eeTag) {
9194                my $t = PrintableTagID($tag,2);
9195                if ($size > 0x8000000) {
9196                    $et->Warn("Skipping '${t}' atom > 128 MB", 1);
9197                } else {
9198                    $et->Warn("Skipping '${t}' atom > 32 MB", 2) or $ignore = 0;
9199                }
9200            }
9201        }
9202        if (defined $tagInfo and not $ignore) {
9203            # set document number for this item property if necessary
9204            if ($$et{IsItemProperty}) {
9205                my $items = $$et{ItemInfo};
9206                my ($id, $prop, $docNum, $lowest);
9207                my $primary = $$et{PrimaryItem} || 0;
9208ItemID:         foreach $id (keys %$items) {
9209                    next unless $$items{$id}{Association};
9210                    my $item = $$items{$id};
9211                    foreach $prop (@{$$item{Association}}) {
9212                        next unless $prop == $index;
9213                        if ($id == $primary or (not $dontInherit{$tag} and
9214                            (not $$item{RefersTo} or $$item{RefersTo}{$primary})))
9215                        {
9216                            # this is associated with the primary item or an item describing
9217                            # the primary item, so consider this part of the main document
9218                            undef $docNum;
9219                            undef $lowest;
9220                            last ItemID;
9221                        } elsif ($$item{DocNum}) {
9222                            # this property is already associated with an item that has
9223                            # an ExifTool document number, so use the lowest associated DocNum
9224                            $docNum = $$item{DocNum} if not defined $docNum or $docNum > $$item{DocNum};
9225                        } elsif (not defined $lowest or $lowest > $id) {
9226                            # keep track of the lowest associated item ID
9227                            $lowest = $id;
9228                        }
9229                    }
9230                }
9231                if (not defined $docNum and defined $lowest) {
9232                    # this is the first time we've seen metadata from this item,
9233                    # so use a new document number
9234                    $docNum = ++$$et{DOC_COUNT};
9235                    $$items{$lowest}{DocNum} = $docNum;
9236                }
9237                $$et{DOC_NUM} = $docNum;
9238            }
9239            my $val;
9240            my $missing = $size - $raf->Read($val, $size);
9241            if ($missing) {
9242                my $t = PrintableTagID($tag,2);
9243                $et->Warn("Truncated '${t}' data (missing $missing bytes)");
9244                last;
9245            }
9246            # use value to get tag info if necessary
9247            $tagInfo or $tagInfo = $et->GetTagInfo($tagTablePtr, $tag, \$val);
9248            my $hasData = ($$dirInfo{HasData} and $val =~ /\0...data\0/s);
9249            if ($verbose and not $hasData) {
9250                my $tval;
9251                if ($tagInfo and $$tagInfo{Format}) {
9252                    $tval = ReadValue(\$val, 0, $$tagInfo{Format}, $$tagInfo{Count}, length($val));
9253                }
9254                $et->VerboseInfo($tag, $tagInfo,
9255                    Value   => $tval,
9256                    DataPt  => \$val,
9257                    DataPos => $dataPos,
9258                    Size    => $size,
9259                    Format  => $tagInfo ? $$tagInfo{Format} : undef,
9260                    Index   => $index,
9261                );
9262                # print iref item ID numbers
9263                if ($dirID eq 'iref') {
9264                    my ($id, $count, @to, $i);
9265                    if ($$et{ItemRefVersion}) {
9266                        ($id, $count, @to) = unpack('NnN*', $val) if length $val >= 10;
9267                    } else {
9268                        ($id, $count, @to) = unpack('nnn*', $val) if length $val >= 6;
9269                    }
9270                    defined $id or $id = '<err>', $count = 0;
9271                    $id .= " (wrong count: $count)" if $count != @to;
9272                    # convert sequential numbers to a range
9273                    for ($i=1; $i<@to; ) {
9274                        $to[$i-1] =~ /(\d+)$/ and $to[$i] == $1 + 1 or ++$i, next;
9275                        $to[$i-1] =~ s/(-.*)?$/-$to[$i]/;
9276                        splice @to, $i, 1;
9277                    }
9278                    $et->VPrint(1, "$$et{INDENT}  Item $id refers to: ",join(',',@to),"\n");
9279                }
9280            }
9281            # extract metadata from stream if ExtractEmbedded option is enabled
9282            if ($eeTag) {
9283                ParseTag($et, $tag, \$val);
9284                # forget this tag if we generated it only for ExtractEmbedded
9285                undef $tagInfo if $tagInfo and $$tagInfo{Unknown} and not $unkOpt;
9286            }
9287
9288            # handle iTunesInfo mean/name/data triplets
9289            if ($tagInfo and $$tagInfo{Triplet}) {
9290                if ($tag eq 'data' and $triplet{mean} and $triplet{name}) {
9291                    $tag = $triplet{name};
9292                    # add 'mean' to name unless it is 'com.apple.iTunes'
9293                    $tag = $triplet{mean} . '/' . $tag unless $triplet{mean} eq 'com.apple.iTunes';
9294                    $tagInfo = $et->GetTagInfo($tagTablePtr, $tag, \$val);
9295                    unless ($tagInfo) {
9296                        my $name = $triplet{name};
9297                        my $desc = $name;
9298                        $name =~ tr/-_a-zA-Z0-9//dc;
9299                        $desc =~ tr/_/ /;
9300                        $tagInfo = {
9301                            Name => $name,
9302                            Description => $desc,
9303                        };
9304                        AddTagToTable($tagTablePtr, $tag, $tagInfo);
9305                    }
9306                    # ignore 8-byte header
9307                    $val = substr($val, 8) if length($val) >= 8;
9308                    unless ($$tagInfo{Format} or $$tagInfo{SubDirectory}) {
9309                        # extract as binary if it contains any non-ASCII or control characters
9310                        if ($val =~ /[^\x20-\x7e]/) {
9311                            my $buff = $val;
9312                            $val = \$buff;
9313                        }
9314                    }
9315                    undef %triplet;
9316                } else {
9317                    undef %triplet if $tag eq 'mean';
9318                    $triplet{$tag} = substr($val,4) if length($val) > 4;
9319                    undef $tagInfo;  # don't store this tag
9320                }
9321            }
9322            if ($tagInfo) {
9323                my $subdir = $$tagInfo{SubDirectory};
9324                if ($subdir) {
9325                    my $start = $$subdir{Start} || 0;
9326                    my ($base, $dPos) = ($dataPos, 0);
9327                    if ($$subdir{Base}) {
9328                        $dPos -= eval $$subdir{Base};
9329                        $base -= $dPos;
9330                    }
9331                    my %dirInfo = (
9332                        DataPt     => \$val,
9333                        DataLen    => $size,
9334                        DirStart   => $start,
9335                        DirLen     => $size - $start,
9336                        DirName    => $$subdir{DirName} || $$tagInfo{Name},
9337                        DirID      => $tag,
9338                        HasData    => $$subdir{HasData},
9339                        Multi      => $$subdir{Multi},
9340                        IgnoreProp => $$subdir{IgnoreProp}, # (XML hack)
9341                        DataPos    => $dPos,
9342                        Base       => $base, # (needed for IsOffset tags in binary data)
9343                    );
9344                    $dirInfo{BlockInfo} = $tagInfo if $$tagInfo{BlockExtract};
9345                    if ($$subdir{ByteOrder} and $$subdir{ByteOrder} =~ /^Little/) {
9346                        SetByteOrder('II');
9347                    }
9348                    my $oldGroup1 = $$et{SET_GROUP1};
9349                    if ($$tagInfo{SubDirectory} and $$tagInfo{SubDirectory}{TagTable} and
9350                        $$tagInfo{SubDirectory}{TagTable} eq 'Image::ExifTool::QuickTime::Track')
9351                    {
9352                        $track or $track = 0;
9353                        $$et{SET_GROUP1} = 'Track' . (++$track);
9354                    }
9355                    my $subTable = GetTagTable($$subdir{TagTable});
9356                    my $proc = $$subdir{ProcessProc};
9357                    # make ProcessMOV() the default processing procedure for subdirectories
9358                    $proc = \&ProcessMOV unless $proc or $$subTable{PROCESS_PROC};
9359                    if ($size > $start) {
9360                        # delay processing of ipco box until after all other boxes
9361                        if ($tag eq 'ipco' and not $$et{IsItemProperty}) {
9362                            $$et{ItemPropertyContainer} = [ \%dirInfo, $subTable, $proc ];
9363                            $et->VPrint(0,"$$et{INDENT}\[Process ipco box later]");
9364                        } else {
9365                            $et->ProcessDirectory(\%dirInfo, $subTable, $proc);
9366                        }
9367                    }
9368                    if ($tag eq 'stbl') {
9369                        # process sample data when exiting SampleTable box if extracting embedded
9370                        ProcessSamples($et) if $ee;
9371                    } elsif ($tag eq 'minf') {
9372                        $$et{HandlerType} = ''; # reset handler type at end of media info box
9373                    }
9374                    $$et{SET_GROUP1} = $oldGroup1;
9375                    SetByteOrder('MM');
9376                } elsif ($hasData) {
9377                    # handle atoms containing 'data' tags
9378                    # (currently ignore contained atoms: 'itif', 'name', etc.)
9379                    my $pos = 0;
9380                    for (;;) {
9381                        last if $pos + 16 > $size;
9382                        my ($len, $type, $flags, $ctry, $lang) = unpack("x${pos}Na4Nnn", $val);
9383                        last if $pos + $len > $size;
9384                        my ($value, $langInfo, $oldDir);
9385                        my $format = $$tagInfo{Format};
9386                        if ($type eq 'data' and $len >= 16) {
9387                            $pos += 16;
9388                            $len -= 16;
9389                            $value = substr($val, $pos, $len);
9390                            # format flags (ref 12):
9391                            # 0x0=binary, 0x1=UTF-8, 0x2=UTF-16, 0x3=ShiftJIS,
9392                            # 0x4=UTF-8  0x5=UTF-16, 0xd=JPEG, 0xe=PNG,
9393                            # 0x15=signed int, 0x16=unsigned int, 0x17=float,
9394                            # 0x18=double, 0x1b=BMP, 0x1c='meta' atom
9395                            if ($stringEncoding{$flags}) {
9396                                # handle all string formats
9397                                $value = $et->Decode($value, $stringEncoding{$flags});
9398                                # (shouldn't be null terminated, but some software writes it anyway)
9399                                $value =~ s/\0$// unless $$tagInfo{Binary};
9400                            } else {
9401                                if (not $format) {
9402                                    $format = QuickTimeFormat($flags, $len);
9403                                } elsif ($format =~ /^int\d+([us])$/) {
9404                                    # adjust integer to available length (but not int64)
9405                                    my $fmt = { 1=>'int8', 2=>'int16', 4=>'int32' }->{$len};
9406                                    $format = $fmt . $1 if defined $fmt;
9407                                }
9408                                if ($format) {
9409                                    $value = ReadValue(\$value, 0, $format, $$tagInfo{Count}, $len);
9410                                } elsif (not $$tagInfo{ValueConv}) {
9411                                    # make binary data a scalar reference unless a ValueConv exists
9412                                    my $buf = $value;
9413                                    $value = \$buf;
9414                                }
9415                            }
9416                        }
9417                        if ($ctry or $lang) {
9418                            my $langCode = GetLangCode($lang, $ctry);
9419                            if ($langCode) {
9420                                # get tagInfo for other language
9421                                $langInfo = GetLangInfoQT($et, $tagInfo, $langCode);
9422                                # save other language tag ID's so we can delete later if necessary
9423                                if ($langInfo) {
9424                                    $$tagInfo{OtherLang} or $$tagInfo{OtherLang} = [ ];
9425                                    push @{$$tagInfo{OtherLang}}, $$langInfo{TagID};
9426                                }
9427                            }
9428                        }
9429                        $langInfo or $langInfo = $tagInfo;
9430                        $et->VerboseInfo($tag, $langInfo,
9431                            Value   => ref $value ? $$value : $value,
9432                            DataPt  => \$val,
9433                            DataPos => $dataPos,
9434                            Start   => $pos,
9435                            Size    => $len,
9436                            Format  => $format,
9437                            Index   => $index,
9438                            Extra   => sprintf(", Type='${type}', Flags=0x%x, Lang=0x%.4x",$flags,$lang),
9439                        ) if $verbose;
9440                        # use "Keys" in path instead of ItemList if this was defined by a Keys tag
9441                        my $isKey = $$tagInfo{Groups} && $$tagInfo{Groups}{1} && $$tagInfo{Groups}{1} eq 'Keys';
9442                        if ($isKey) {
9443                            $oldDir = $$et{PATH}[-1];
9444                            $$et{PATH}[-1] = 'Keys';
9445                        }
9446                        $et->FoundTag($langInfo, $value) if defined $value;
9447                        $$et{PATH}[-1] = $oldDir if $isKey;
9448                        $pos += $len;
9449                    }
9450                } elsif ($tag =~ /^\xa9/ or $$tagInfo{IText}) {
9451                    # parse international text to extract all languages
9452                    my $pos = 0;
9453                    if ($$tagInfo{Format}) {
9454                        $et->FoundTag($tagInfo, ReadValue(\$val, 0, $$tagInfo{Format}, undef, length($val)));
9455                        $pos = $size;
9456                    }
9457                    for (;;) {
9458                        my ($len, $lang);
9459                        if ($$tagInfo{IText} and $$tagInfo{IText} >= 6) {
9460                            last if $pos + $$tagInfo{IText} > $size;
9461                            $pos += $$tagInfo{IText} - 2;
9462                            $lang = unpack("x${pos}n", $val);
9463                            $pos += 2;
9464                            $len = $size - $pos;
9465                        } else {
9466                            last if $pos + 4 > $size;
9467                            ($len, $lang) = unpack("x${pos}nn", $val);
9468                            $pos += 4;
9469                            # according to the QuickTime spec (ref 12), $len should include
9470                            # 4 bytes for length and type words, but nobody (including
9471                            # Apple, Pentax and Kodak) seems to add these in, so try
9472                            # to allow for either
9473                            if ($pos + $len > $size) {
9474                                $len -= 4;
9475                                last if $pos + $len > $size or $len < 0;
9476                            }
9477                        }
9478                        # ignore any empty entries (or null padding) after the first
9479                        next if not $len and $pos;
9480                        my $str = substr($val, $pos, $len);
9481                        my ($langInfo, $enc);
9482                        if (($lang < 0x400 or $lang == 0x7fff) and $str !~ /^\xfe\xff/) {
9483                            # this is a Macintosh language code
9484                            # a language code of 0 is Macintosh english, so treat as default
9485                            if ($lang) {
9486                                if ($lang == 0x7fff) {
9487                                    # technically, ISO 639-2 doesn't have a 2-character
9488                                    # equivalent for 'und', but use 'un' anyway
9489                                    $lang = 'un';
9490                                } else {
9491                                    # use Font.pm to look up language string
9492                                    require Image::ExifTool::Font;
9493                                    $lang = $Image::ExifTool::Font::ttLang{Macintosh}{$lang};
9494                                }
9495                            }
9496                            # the spec says only "Macintosh text encoding", but
9497                            # allow this to be configured by the user
9498                            $enc = $charsetQuickTime;
9499                        } else {
9500                            # convert language code to ASCII (ignore read-only bit)
9501                            $lang = UnpackLang($lang);
9502                            # may be either UTF-8 or UTF-16BE
9503                            $enc = $str=~s/^\xfe\xff// ? 'UTF16' : 'UTF8';
9504                        }
9505                        unless ($$tagInfo{NoDecode}) {
9506                            $str = $et->Decode($str, $enc);
9507                            $str =~ s/\0+$//;   # remove any trailing nulls (eg. 3gp tags)
9508                        }
9509                        if ($$tagInfo{IText} and $$tagInfo{IText} > 6) {
9510                            my $n = $$tagInfo{IText} - 6;
9511                            # add back extra bytes (eg. 'rtng' box)
9512                            $str = substr($val, $pos-$n-2, $n) . $str;
9513                        }
9514                        $langInfo = GetLangInfoQT($et, $tagInfo, $lang) if $lang;
9515                        $et->FoundTag($langInfo || $tagInfo, $str);
9516                        $pos += $len;
9517                    }
9518                } else {
9519                    my $format = $$tagInfo{Format};
9520                    if ($format) {
9521                        $val = ReadValue(\$val, 0, $format, $$tagInfo{Count}, length($val));
9522                    }
9523                    my $oldBase;
9524                    if ($$tagInfo{SetBase}) {
9525                        $oldBase = $$et{BASE};
9526                        $$et{BASE} = $dataPos;
9527                    }
9528                    my $key = $et->FoundTag($tagInfo, $val);
9529                    $$et{BASE} = $oldBase if defined $oldBase;
9530                    # decode if necessary (NOTE: must be done after RawConv)
9531                    if (defined $key and (not $format or $format =~ /^string/) and
9532                        not $$tagInfo{Unknown} and not $$tagInfo{ValueConv} and
9533                        not $$tagInfo{Binary} and defined $$et{VALUE}{$key} and not ref $val)
9534                    {
9535                        my $vp = \$$et{VALUE}{$key};
9536                        if (not ref $$vp and length($$vp) <= 65536 and $$vp =~ /[\x80-\xff]/) {
9537                            # the encoding of this is not specified, so use CharsetQuickTime
9538                            # unless the string is valid UTF-8
9539                            require Image::ExifTool::XMP;
9540                            my $enc = Image::ExifTool::XMP::IsUTF8($vp) > 0 ? 'UTF8' : $charsetQuickTime;
9541                            $$vp = $et->Decode($$vp, $enc);
9542                        }
9543                    }
9544                }
9545            }
9546        } else {
9547            $et->VerboseInfo($tag, $tagInfo,
9548                Size  => $size,
9549                Extra => sprintf(' at offset 0x%.4x', $raf->Tell()),
9550            ) if $verbose;
9551            if ($size and (not $raf->Seek($size-1, 1) or $raf->Read($buff, 1) != 1)) {
9552                my $t = PrintableTagID($tag,2);
9553                $et->Warn("Truncated '${t}' data");
9554                last;
9555            }
9556        }
9557        $dataPos += $size + 8;  # point to start of next atom data
9558        last if $dirEnd and $dataPos >= $dirEnd; # (note: ignores last value if 0 bytes)
9559        $raf->Read($buff, 8) == 8 or last;
9560        ($size, $tag) = unpack('Na4', $buff);
9561        ++$index if defined $index;
9562    }
9563    # tweak file type based on track content ("iso*" and "dash" ftyp only)
9564    if ($topLevel and $$et{VALUE}{FileType} and $$et{VALUE}{FileType} eq 'MP4' and
9565        $$et{save_ftyp} and $$et{HasHandler} and $$et{save_ftyp} =~ /^(iso|dash)/ and
9566        $$et{HasHandler}{soun} and not $$et{HasHandler}{vide})
9567    {
9568        $et->OverrideFileType('M4A', 'audio/mp4');
9569    }
9570    # fill in missing defaults for alternate language tags
9571    # (the first language is taken as the default)
9572    if ($doDefaultLang and $$et{QTLang}) {
9573QTLang: foreach $tag (@{$$et{QTLang}}) {
9574            next unless defined $$et{VALUE}{$tag};
9575            my $langInfo = $$et{TAG_INFO}{$tag} or next;
9576            my $tagInfo = $$langInfo{SrcTagInfo} or next;
9577            my $infoHash = $$et{TAG_INFO};
9578            my $name = $$tagInfo{Name};
9579            # loop through all instances of this tag name and generate the default-language
9580            # version only if we don't already have a QuickTime tag with this name
9581            my ($i, $key);
9582            for ($i=0, $key=$name; $$infoHash{$key}; ++$i, $key="$name ($i)") {
9583                next QTLang if $et->GetGroup($key, 0) eq 'QuickTime';
9584            }
9585            $et->FoundTag($tagInfo, $$et{VALUE}{$tag});
9586        }
9587        delete $$et{QTLang};
9588    }
9589    # process item information now that we are done processing its 'meta' container
9590    HandleItemInfo($et) if $topLevel or $dirID eq 'meta';
9591
9592    ScanMediaData($et) if $ee and $topLevel;  # brute force scan for metadata embedded in media data
9593
9594    # restore any changed options
9595    $et->Options($_ => $saveOptions{$_}) foreach keys %saveOptions;
9596    return 1;
9597}
9598
9599#------------------------------------------------------------------------------
9600# Process a QuickTime Image File
9601# Inputs: 0) ExifTool object reference, 1) directory information reference
9602# Returns: 1 on success
9603sub ProcessQTIF($$)
9604{
9605    my ($et, $dirInfo) = @_;
9606    my $table = GetTagTable('Image::ExifTool::QuickTime::ImageFile');
9607    return ProcessMOV($et, $dirInfo, $table);
9608}
9609
96101;  # end
9611
9612__END__
9613
9614=head1 NAME
9615
9616Image::ExifTool::QuickTime - Read QuickTime and MP4 meta information
9617
9618=head1 SYNOPSIS
9619
9620This module is used by Image::ExifTool
9621
9622=head1 DESCRIPTION
9623
9624This module contains routines required by Image::ExifTool to extract
9625information from QuickTime and MP4 video, M4A audio, and HEIC image files.
9626
9627=head1 AUTHOR
9628
9629Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com)
9630
9631This library is free software; you can redistribute it and/or modify it
9632under the same terms as Perl itself.
9633
9634=head1 REFERENCES
9635
9636=over 4
9637
9638=item L<http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html>
9639
9640=item L<http://search.cpan.org/dist/MP4-Info-1.04/>
9641
9642=item L<http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt>
9643
9644=item L<http://wiki.multimedia.cx/index.php?title=Apple_QuickTime>
9645
9646=item L<http://atomicparsley.sourceforge.net/mpeg-4files.html>
9647
9648=item L<http://wiki.multimedia.cx/index.php?title=QuickTime_container>
9649
9650=item L<http://code.google.com/p/mp4v2/wiki/iTunesMetadata>
9651
9652=item L<http://www.canieti.com.mx/assets/files/1011/IEC_100_1384_DC.pdf>
9653
9654=item L<http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf>
9655
9656=back
9657
9658=head1 SEE ALSO
9659
9660L<Image::ExifTool::TagNames/QuickTime Tags>,
9661L<Image::ExifTool(3pm)|Image::ExifTool>
9662
9663=cut
9664
9665