File Coverage

blib/lib/Image/ExifTool/QuickTime.pm
Criterion Covered Total %
statement 648 1011 64.0
branch 338 724 46.6
condition 184 403 45.6
subroutine 29 39 74.3
pod 0 32 0.0
total 1199 2209 54.2


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: QuickTime.pm
3             #
4             # Description: Read QuickTime and MP4 meta information
5             #
6             # Revisions: 10/04/2005 - P. Harvey Created
7             # 12/19/2005 - P. Harvey Added MP4 support
8             # 09/22/2006 - P. Harvey Added M4A support
9             # 07/27/2010 - P. Harvey Updated to 2010-05-03 QuickTime spec
10             #
11             # References:
12             #
13             # 1) http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html
14             # 2) http://search.cpan.org/dist/MP4-Info-1.04/
15             # 3) http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
16             # 4) http://wiki.multimedia.cx/index.php?title=Apple_QuickTime
17             # 5) ISO 14496-12 (http://read.pudn.com/downloads64/ebook/226547/ISO_base_media_file_format.pdf)
18             # 6) ISO 14496-16 (http://www.iec-normen.de/previewpdf/info_isoiec14496-16%7Bed2.0%7Den.pdf)
19             # 7) http://atomicparsley.sourceforge.net/mpeg-4files.html
20             # 8) http://wiki.multimedia.cx/index.php?title=QuickTime_container
21             # 9) http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart3.pdf (Oct 2008)
22             # 10) http://code.google.com/p/mp4v2/wiki/iTunesMetadata
23             # 11) http://www.canieti.com.mx/assets/files/1011/IEC_100_1384_DC.pdf
24             # 12) QuickTime file format specification 2010-05-03
25             # 13) http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf
26             # 14) http://standards.iso.org/ittf/PubliclyAvailableStandards/c051533_ISO_IEC_14496-12_2008.zip
27             # 15) http://getid3.sourceforge.net/source/module.audio-video.quicktime.phps
28             # 16) http://qtra.apple.com/atoms.html
29             # 17) http://www.etsi.org/deliver/etsi_ts/126200_126299/126244/10.01.00_60/ts_126244v100100p.pdf
30             # 18) https://github.com/appsec-labs/iNalyzer/blob/master/scinfo.m
31             # 19) http://nah6.com/~itsme/cvs-xdadevtools/iphone/tools/decodesinf.pl
32             # 20) https://developer.apple.com/legacy/library/documentation/quicktime/reference/QT7-1_Update_Reference/QT7-1_Update_Reference.pdf
33             # 21) Francois Bonzon private communication
34             # 22) https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html
35             # 23) http://atomicparsley.sourceforge.net/mpeg-4files.html
36             # 24) https://github.com/sergiomb2/libmp4v2/wiki/iTunesMetadata
37             # 25) https://cconcolato.github.io/mp4ra/atoms.html
38             # 26) https://github.com/SamsungVR/android_upload_sdk/blob/master/SDKLib/src/main/java/com/samsung/msca/samsungvr/sdk/UserVideo.java
39             # 27) https://exiftool.org/forum/index.php?topic=11517.0
40             #------------------------------------------------------------------------------
41              
42             package Image::ExifTool::QuickTime;
43              
44 30     30   6033 use strict;
  30         84  
  30         1205  
45 30     30   201 use vars qw($VERSION $AUTOLOAD %stringEncoding);
  30         92  
  30         1894  
46 30     30   216 use Image::ExifTool qw(:DataAccess :Utils);
  30         81  
  30         6320  
47 30     30   2820 use Image::ExifTool::Exif;
  30         133  
  30         959  
48 30     30   1857 use Image::ExifTool::GPS;
  30         97  
  30         452430  
49              
50             $VERSION = '2.84';
51              
52             sub ProcessMOV($$;$);
53             sub ProcessKeys($$$);
54             sub ProcessMetaKeys($$$);
55             sub ProcessMetaData($$$);
56             sub ProcessEncodingParams($$$);
57             sub ProcessSampleDesc($$$);
58             sub ProcessHybrid($$$);
59             sub ProcessRights($$$);
60             # ++vvvvvvvvvvvv++ (in QuickTimeStream.pl)
61             sub Process_mebx($$$);
62             sub Process_3gf($$$);
63             sub Process_gps0($$$);
64             sub Process_gsen($$$);
65             sub ProcessKenwood($$$);
66             sub ProcessRIFFTrailer($$$);
67             sub ProcessTTAD($$$);
68             sub ProcessNMEA($$$);
69             sub ProcessGPSLog($$$);
70             sub ProcessGarminGPS($$$);
71             sub SaveMetaKeys($$$);
72             # ++^^^^^^^^^^^^++
73             sub ParseItemLocation($$);
74             sub ParseContentDescribes($$);
75             sub ParseItemInfoEntry($$);
76             sub ParseItemPropAssoc($$);
77             sub FixWrongFormat($);
78             sub GetMatrixStructure($$);
79             sub ConvertISO6709($);
80             sub ConvInvISO6709($);
81             sub ConvertChapterList($);
82             sub PrintChapter($);
83             sub PrintGPSCoordinates($);
84             sub PrintInvGPSCoordinates($);
85             sub UnpackLang($;$);
86             sub WriteKeys($$$);
87             sub WriteQuickTime($$$);
88             sub WriteMOV($$);
89             sub GetLangInfo($$);
90             sub CheckQTValue($$$);
91              
92             # MIME types for all entries in the ftypLookup with file extensions
93             # (defaults to 'video/mp4' if not found in this lookup)
94             my %mimeLookup = (
95             '3G2' => 'video/3gpp2',
96             '3GP' => 'video/3gpp',
97             AAX => 'audio/vnd.audible.aax',
98             DVB => 'video/vnd.dvb.file',
99             F4A => 'audio/mp4',
100             F4B => 'audio/mp4',
101             JP2 => 'image/jp2',
102             JPM => 'image/jpm',
103             JPX => 'image/jpx',
104             M4A => 'audio/mp4',
105             M4B => 'audio/mp4',
106             M4P => 'audio/mp4',
107             M4V => 'video/x-m4v',
108             MOV => 'video/quicktime',
109             MQV => 'video/quicktime',
110             HEIC => 'image/heic',
111             HEVC => 'image/heic-sequence',
112             HEICS=> 'image/heic-sequence',
113             HEIF => 'image/heif',
114             HEIFS=> 'image/heif-sequence',
115             AVIF => 'image/avif', #PH (NC)
116             CRX => 'video/x-canon-crx', # (will get overridden)
117             );
118              
119             # look up file type from ftyp atom type, with MIME type in comment if known
120             # (ref http://www.ftyps.com/)
121             my %ftypLookup = (
122             '3g2a' => '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-0 V1.0', # video/3gpp2
123             '3g2b' => '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-A V1.0.0', # video/3gpp2
124             '3g2c' => '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-B v1.0', # video/3gpp2
125             '3ge6' => '3GPP (.3GP) Release 6 MBMS Extended Presentations', # video/3gpp
126             '3ge7' => '3GPP (.3GP) Release 7 MBMS Extended Presentations', # video/3gpp
127             '3gg6' => '3GPP Release 6 General Profile', # video/3gpp
128             '3gp1' => '3GPP Media (.3GP) Release 1 (probably non-existent)', # video/3gpp
129             '3gp2' => '3GPP Media (.3GP) Release 2 (probably non-existent)', # video/3gpp
130             '3gp3' => '3GPP Media (.3GP) Release 3 (probably non-existent)', # video/3gpp
131             '3gp4' => '3GPP Media (.3GP) Release 4', # video/3gpp
132             '3gp5' => '3GPP Media (.3GP) Release 5', # video/3gpp
133             '3gp6' => '3GPP Media (.3GP) Release 6 Basic Profile', # video/3gpp
134             '3gp6' => '3GPP Media (.3GP) Release 6 Progressive Download', # video/3gpp
135             '3gp6' => '3GPP Media (.3GP) Release 6 Streaming Servers', # video/3gpp
136             '3gs7' => '3GPP Media (.3GP) Release 7 Streaming Servers', # video/3gpp
137             'aax ' => 'Audible Enhanced Audiobook (.AAX)', #PH
138             'avc1' => 'MP4 Base w/ AVC ext [ISO 14496-12:2005]', # video/mp4
139             'CAEP' => 'Canon Digital Camera',
140             'caqv' => 'Casio Digital Camera',
141             'CDes' => 'Convergent Design',
142             'da0a' => 'DMB MAF w/ MPEG Layer II aud, MOT slides, DLS, JPG/PNG/MNG images',
143             'da0b' => 'DMB MAF, extending DA0A, with 3GPP timed text, DID, TVA, REL, IPMP',
144             'da1a' => 'DMB MAF audio with ER-BSAC audio, JPG/PNG/MNG images',
145             'da1b' => 'DMB MAF, extending da1a, with 3GPP timed text, DID, TVA, REL, IPMP',
146             'da2a' => 'DMB MAF aud w/ HE-AAC v2 aud, MOT slides, DLS, JPG/PNG/MNG images',
147             'da2b' => 'DMB MAF, extending da2a, with 3GPP timed text, DID, TVA, REL, IPMP',
148             'da3a' => 'DMB MAF aud with HE-AAC aud, JPG/PNG/MNG images',
149             'da3b' => 'DMB MAF, extending da3a w/ BIFS, 3GPP timed text, DID, TVA, REL, IPMP',
150             'dmb1' => 'DMB MAF supporting all the components defined in the specification',
151             'dmpf' => 'Digital Media Project', # various
152             'drc1' => 'Dirac (wavelet compression), encapsulated in ISO base media (MP4)',
153             'dv1a' => 'DMB MAF vid w/ AVC vid, ER-BSAC aud, BIFS, JPG/PNG/MNG images, TS',
154             'dv1b' => 'DMB MAF, extending dv1a, with 3GPP timed text, DID, TVA, REL, IPMP',
155             'dv2a' => 'DMB MAF vid w/ AVC vid, HE-AAC v2 aud, BIFS, JPG/PNG/MNG images, TS',
156             'dv2b' => 'DMB MAF, extending dv2a, with 3GPP timed text, DID, TVA, REL, IPMP',
157             'dv3a' => 'DMB MAF vid w/ AVC vid, HE-AAC aud, BIFS, JPG/PNG/MNG images, TS',
158             'dv3b' => 'DMB MAF, extending dv3a, with 3GPP timed text, DID, TVA, REL, IPMP',
159             'dvr1' => 'DVB (.DVB) over RTP', # video/vnd.dvb.file
160             'dvt1' => 'DVB (.DVB) over MPEG-2 Transport Stream', # video/vnd.dvb.file
161             'F4A ' => 'Audio for Adobe Flash Player 9+ (.F4A)', # audio/mp4
162             'F4B ' => 'Audio Book for Adobe Flash Player 9+ (.F4B)', # audio/mp4
163             'F4P ' => 'Protected Video for Adobe Flash Player 9+ (.F4P)', # video/mp4
164             'F4V ' => 'Video for Adobe Flash Player 9+ (.F4V)', # video/mp4
165             'isc2' => 'ISMACryp 2.0 Encrypted File', # ?/enc-isoff-generic
166             'iso2' => 'MP4 Base Media v2 [ISO 14496-12:2005]', # video/mp4 (or audio)
167             'iso3' => 'MP4 Base Media v3', # video/mp4 (or audio)
168             'iso4' => 'MP4 Base Media v4', # video/mp4 (or audio)
169             'iso5' => 'MP4 Base Media v5', # video/mp4 (or audio)
170             'iso6' => 'MP4 Base Media v6', # video/mp4 (or audio)
171             'iso7' => 'MP4 Base Media v7', # video/mp4 (or audio)
172             'iso8' => 'MP4 Base Media v8', # video/mp4 (or audio)
173             'iso9' => 'MP4 Base Media v9', # video/mp4 (or audio)
174             'isom' => 'MP4 Base Media v1 [IS0 14496-12:2003]', # video/mp4 (or audio)
175             'JP2 ' => 'JPEG 2000 Image (.JP2) [ISO 15444-1 ?]', # image/jp2
176             'JP20' => 'Unknown, from GPAC samples (prob non-existent)',
177             'jpm ' => 'JPEG 2000 Compound Image (.JPM) [ISO 15444-6]', # image/jpm
178             'jpx ' => 'JPEG 2000 with extensions (.JPX) [ISO 15444-2]', # image/jpx
179             'KDDI' => '3GPP2 EZmovie for KDDI 3G cellphones', # video/3gpp2
180             #LCAG => (found in CompatibleBrands of Leica MOV videos)
181             'M4A ' => 'Apple iTunes AAC-LC (.M4A) Audio', # audio/x-m4a
182             'M4B ' => 'Apple iTunes AAC-LC (.M4B) Audio Book', # audio/mp4
183             'M4P ' => 'Apple iTunes AAC-LC (.M4P) AES Protected Audio', # audio/mp4
184             'M4V ' => 'Apple iTunes Video (.M4V) Video', # video/x-m4v
185             'M4VH' => 'Apple TV (.M4V)', # video/x-m4v
186             'M4VP' => 'Apple iPhone (.M4V)', # video/x-m4v
187             'mj2s' => 'Motion JPEG 2000 [ISO 15444-3] Simple Profile', # video/mj2
188             'mjp2' => 'Motion JPEG 2000 [ISO 15444-3] General Profile', # video/mj2
189             'mmp4' => 'MPEG-4/3GPP Mobile Profile (.MP4/3GP) (for NTT)', # video/mp4
190             'mp21' => 'MPEG-21 [ISO/IEC 21000-9]', # various
191             'mp41' => 'MP4 v1 [ISO 14496-1:ch13]', # video/mp4
192             'mp42' => 'MP4 v2 [ISO 14496-14]', # video/mp4
193             'mp71' => 'MP4 w/ MPEG-7 Metadata [per ISO 14496-12]', # various
194             'MPPI' => 'Photo Player, MAF [ISO/IEC 23000-3]', # various
195             'mqt ' => 'Sony / Mobile QuickTime (.MQV) US Patent 7,477,830 (Sony Corp)', # video/quicktime
196             'MSNV' => 'MPEG-4 (.MP4) for SonyPSP', # audio/mp4
197             'NDAS' => 'MP4 v2 [ISO 14496-14] Nero Digital AAC Audio', # audio/mp4
198             'NDSC' => 'MPEG-4 (.MP4) Nero Cinema Profile', # video/mp4
199             'NDSH' => 'MPEG-4 (.MP4) Nero HDTV Profile', # video/mp4
200             'NDSM' => 'MPEG-4 (.MP4) Nero Mobile Profile', # video/mp4
201             'NDSP' => 'MPEG-4 (.MP4) Nero Portable Profile', # video/mp4
202             'NDSS' => 'MPEG-4 (.MP4) Nero Standard Profile', # video/mp4
203             'NDXC' => 'H.264/MPEG-4 AVC (.MP4) Nero Cinema Profile', # video/mp4
204             'NDXH' => 'H.264/MPEG-4 AVC (.MP4) Nero HDTV Profile', # video/mp4
205             'NDXM' => 'H.264/MPEG-4 AVC (.MP4) Nero Mobile Profile', # video/mp4
206             'NDXP' => 'H.264/MPEG-4 AVC (.MP4) Nero Portable Profile', # video/mp4
207             'NDXS' => 'H.264/MPEG-4 AVC (.MP4) Nero Standard Profile', # video/mp4
208             'odcf' => 'OMA DCF DRM Format 2.0 (OMA-TS-DRM-DCF-V2_0-20060303-A)', # various
209             'opf2' => 'OMA PDCF DRM Format 2.1 (OMA-TS-DRM-DCF-V2_1-20070724-C)',
210             'opx2' => 'OMA PDCF DRM + XBS extensions (OMA-TS-DRM_XBS-V1_0-20070529-C)',
211             'pana' => 'Panasonic Digital Camera',
212             'qt ' => 'Apple QuickTime (.MOV/QT)', # video/quicktime
213             'ROSS' => 'Ross Video',
214             'sdv ' => 'SD Memory Card Video', # various?
215             'ssc1' => 'Samsung stereoscopic, single stream',
216             'ssc2' => 'Samsung stereoscopic, dual stream',
217             'XAVC' => 'Sony XAVC', #PH
218             'heic' => 'High Efficiency Image Format HEVC still image (.HEIC)', # image/heic
219             'hevc' => 'High Efficiency Image Format HEVC sequence (.HEICS)', # image/heic-sequence
220             'mif1' => 'High Efficiency Image Format still image (.HEIF)', # image/heif
221             'msf1' => 'High Efficiency Image Format sequence (.HEIFS)', # image/heif-sequence
222             'heix' => 'High Efficiency Image Format still image (.HEIF)', # image/heif (ref PH, Canon 1DXmkIII)
223             'avif' => 'AV1 Image File Format (.AVIF)', # image/avif
224             'crx ' => 'Canon Raw (.CRX)', #PH (CR3 or CRM; use Canon CompressorVersion to decide)
225             );
226              
227             # information for int32u date/time tags (time zero is Jan 1, 1904)
228             my %timeInfo = (
229             Notes => 'converted from UTC to local time if the QuickTimeUTC option is set',
230             Shift => 'Time',
231             Writable => 1,
232             Permanent => 1,
233             DelValue => 0,
234             # It is not uncommon for brain-dead software to use the wrong time zero, it should be
235             # Jan 1, 1904, so assume a time zero of Jan 1, 1970 if the date is before this
236             # Note: This value will be in UTC if generated by a system that is aware of the time zone
237             # (also note: this code is duplicated for the CreateDate tag)
238             RawConv => q{
239             my $offset = (66 * 365 + 17) * 24 * 3600;
240             return $val - $offset if $val >= $offset or $$self{OPTIONS}{QuickTimeUTC};
241             if ($val and not $$self{IsWriting}) {
242             $self->WarnOnce('Patched incorrect time zero for QuickTime date/time tag',1);
243             }
244             return $val;
245             },
246             RawConvInv => q{
247             if ($$self{FileType} eq 'CR3' and not $self->Options('QuickTimeUTC')) {
248             # convert to UTC
249             my $offset = (66 * 365 + 17) * 24 * 3600;
250             $val = ConvertUnixTime($val - $offset);
251             $val = GetUnixTime($val, 1) + $offset;
252             }
253             return $val;
254             },
255             # (all CR3 files store UTC times - PH)
256             ValueConv => 'ConvertUnixTime($val, $self->Options("QuickTimeUTC") || $$self{FileType} eq "CR3")',
257             ValueConvInv => q{
258             $val = GetUnixTime($val, $self->Options("QuickTimeUTC"));
259             return undef unless defined $val;
260             return $val + (66 * 365 + 17) * 24 * 3600;
261             },
262             PrintConv => '$self->ConvertDateTime($val)',
263             PrintConvInv => '$self->InverseDateTime($val)',
264             # (can't put Groups here because they aren't constant!)
265             );
266             # information for duration tags
267             my %durationInfo = (
268             ValueConv => '$$self{TimeScale} ? $val / $$self{TimeScale} : $val',
269             PrintConv => '$$self{TimeScale} ? ConvertDuration($val) : $val',
270             );
271             # handle unknown tags
272             my %unknownInfo = (
273             Unknown => 1,
274             ValueConv => '$val =~ /^([\x20-\x7e]*)\0*$/ ? $1 : \$val',
275             );
276              
277             # multi-language text with 6-byte header
278             my %langText = ( IText => 6 );
279              
280             # parsing for most of the 3gp udta language text boxes
281             my %langText3gp = (
282             Notes => 'used in 3gp videos',
283             Avoid => 1,
284             IText => 6,
285             );
286              
287             # 4-character Vendor ID codes (ref PH)
288             my %vendorID = (
289             appl => 'Apple',
290             fe20 => 'Olympus (fe20)', # (FE200)
291             FFMP => 'FFmpeg',
292             'GIC '=> 'General Imaging Co.',
293             kdak => 'Kodak',
294             KMPI => 'Konica-Minolta',
295             leic => 'Leica',
296             mino => 'Minolta',
297             niko => 'Nikon',
298             NIKO => 'Nikon',
299             olym => 'Olympus',
300             pana => 'Panasonic',
301             pent => 'Pentax',
302             pr01 => 'Olympus (pr01)', # (FE100,FE110,FE115)
303             sany => 'Sanyo',
304             'SMI '=> 'Sorenson Media Inc.',
305             ZORA => 'Zoran Corporation',
306             'AR.D'=> 'Parrot AR.Drone',
307             ' KD '=> 'Kodak', # (FZ201)
308             );
309              
310             # QuickTime data atom encodings for string types (ref 12)
311             %stringEncoding = (
312             1 => 'UTF8',
313             2 => 'UTF16',
314             3 => 'ShiftJIS',
315             4 => 'UTF8',
316             5 => 'UTF16',
317             );
318              
319             my %graphicsMode = (
320             # (ref http://homepage.mac.com/vanhoek/MovieGuts%20docs/64.html)
321             0x00 => 'srcCopy',
322             0x01 => 'srcOr',
323             0x02 => 'srcXor',
324             0x03 => 'srcBic',
325             0x04 => 'notSrcCopy',
326             0x05 => 'notSrcOr',
327             0x06 => 'notSrcXor',
328             0x07 => 'notSrcBic',
329             0x08 => 'patCopy',
330             0x09 => 'patOr',
331             0x0a => 'patXor',
332             0x0b => 'patBic',
333             0x0c => 'notPatCopy',
334             0x0d => 'notPatOr',
335             0x0e => 'notPatXor',
336             0x0f => 'notPatBic',
337             0x20 => 'blend',
338             0x21 => 'addPin',
339             0x22 => 'addOver',
340             0x23 => 'subPin',
341             0x24 => 'transparent',
342             0x25 => 'addMax',
343             0x26 => 'subOver',
344             0x27 => 'addMin',
345             0x31 => 'grayishTextOr',
346             0x32 => 'hilite',
347             0x40 => 'ditherCopy',
348             # the following ref ISO/IEC 15444-3
349             0x100 => 'Alpha',
350             0x101 => 'White Alpha',
351             0x102 => 'Pre-multiplied Black Alpha',
352             0x110 => 'Component Alpha',
353             );
354              
355             my %channelLabel = (
356             0xFFFFFFFF => 'Unknown',
357             0 => 'Unused',
358             100 => 'UseCoordinates',
359             1 => 'Left',
360             2 => 'Right',
361             3 => 'Center',
362             4 => 'LFEScreen',
363             5 => 'LeftSurround',
364             6 => 'RightSurround',
365             7 => 'LeftCenter',
366             8 => 'RightCenter',
367             9 => 'CenterSurround',
368             10 => 'LeftSurroundDirect',
369             11 => 'RightSurroundDirect',
370             12 => 'TopCenterSurround',
371             13 => 'VerticalHeightLeft',
372             14 => 'VerticalHeightCenter',
373             15 => 'VerticalHeightRight',
374             16 => 'TopBackLeft',
375             17 => 'TopBackCenter',
376             18 => 'TopBackRight',
377             33 => 'RearSurroundLeft',
378             34 => 'RearSurroundRight',
379             35 => 'LeftWide',
380             36 => 'RightWide',
381             37 => 'LFE2',
382             38 => 'LeftTotal',
383             39 => 'RightTotal',
384             40 => 'HearingImpaired',
385             41 => 'Narration',
386             42 => 'Mono',
387             43 => 'DialogCentricMix',
388             44 => 'CenterSurroundDirect',
389             45 => 'Haptic',
390             200 => 'Ambisonic_W',
391             201 => 'Ambisonic_X',
392             202 => 'Ambisonic_Y',
393             203 => 'Ambisonic_Z',
394             204 => 'MS_Mid',
395             205 => 'MS_Side',
396             206 => 'XY_X',
397             207 => 'XY_Y',
398             301 => 'HeadphonesLeft',
399             302 => 'HeadphonesRight',
400             304 => 'ClickTrack',
401             305 => 'ForeignLanguage',
402             400 => 'Discrete',
403             0x10000 => 'Discrete_0',
404             0x10001 => 'Discrete_1',
405             0x10002 => 'Discrete_2',
406             0x10003 => 'Discrete_3',
407             0x10004 => 'Discrete_4',
408             0x10005 => 'Discrete_5',
409             0x10006 => 'Discrete_6',
410             0x10007 => 'Discrete_7',
411             0x10008 => 'Discrete_8',
412             0x10009 => 'Discrete_9',
413             0x1000a => 'Discrete_10',
414             0x1000b => 'Discrete_11',
415             0x1000c => 'Discrete_12',
416             0x1000d => 'Discrete_13',
417             0x1000e => 'Discrete_14',
418             0x1000f => 'Discrete_15',
419             0x1ffff => 'Discrete_65535',
420             );
421              
422             my %qtFlags = ( #12
423             0 => 'undef', 22 => 'unsigned int', 71 => 'float[2] size',
424             1 => 'UTF-8', 23 => 'float', 72 => 'float[4] rect',
425             2 => 'UTF-16', 24 => 'double', 74 => 'int64s',
426             3 => 'ShiftJIS', 27 => 'BMP', 75 => 'int8u',
427             4 => 'UTF-8 sort', 28 => 'QT atom', 76 => 'int16u',
428             5 => 'UTF-16 sort', 65 => 'int8s', 77 => 'int32u',
429             13 => 'JPEG', 66 => 'int16s', 78 => 'int64u',
430             14 => 'PNG', 67 => 'int32s', 79 => 'double[3][3]',
431             21 => 'signed int', 70 => 'float[2] point',
432             );
433              
434             # properties which don't get inherited from the parent
435             my %dontInherit = (
436             ispe => 1, # size of parent may be different
437             hvcC => 1, # (likely redundant)
438             );
439              
440             # tags that may be duplicated and directories that may contain duplicate tags
441             # (used only to avoid warnings when Validate-ing)
442             my %dupTagOK = ( mdat => 1, trak => 1, free => 1, infe => 1, sgpd => 1, dimg => 1, CCDT => 1,
443             sbgp => 1, csgm => 1, uuid => 1, cdsc => 1, maxr => 1, '----' => 1 );
444             my %dupDirOK = ( ipco => 1, '----' => 1 );
445              
446             # the usual atoms required to decode timed metadata with the ExtractEmbedded option
447             my %eeStd = ( stco => 'stbl', co64 => 'stbl', stsz => 'stbl', stz2 => 'stbl',
448             stsc => 'stbl', stts => 'stbl' );
449              
450             # atoms required for generating ImageDataMD5
451             my %md5Box = ( vide => { %eeStd }, soun => { %eeStd } );
452              
453             # boxes and their containers for the various handler types that we want to save
454             # when the ExtractEmbedded is enabled (currently only the 'gps ' container name is
455             # used, but others have been checked against all available sample files and may be
456             # useful in the future if the names are used for different boxes on other locations)
457             my %eeBox = (
458             # (note: vide is only processed if specific atoms exist in the VideoSampleDesc)
459             vide => { %eeStd, JPEG => 'stsd' },
460             text => { %eeStd },
461             meta => { %eeStd },
462             sbtl => { %eeStd },
463             data => { %eeStd },
464             camm => { %eeStd }, # (Insta360)
465             ctbx => { %eeStd }, # (GM cars)
466             '' => { 'gps ' => 'moov', 'GPS ' => 'main' }, # (no handler -- in top level 'moov' box, and main)
467             );
468             # boxes to save when ExtractEmbedded is set to 2 or higher
469             my %eeBox2 = (
470             vide => { avcC => 'stsd' }, # (parses H264 video stream)
471             );
472              
473             # QuickTime atoms
474             %Image::ExifTool::QuickTime::Main = (
475             PROCESS_PROC => \&ProcessMOV,
476             WRITE_PROC => \&WriteQuickTime, # (only needs to be defined for directories to process when writing)
477             GROUPS => { 2 => 'Video' },
478             meta => { # 'meta' is found here in my Sony ILCE-7S MP4 sample - PH
479             Name => 'Meta',
480             SubDirectory => {
481             TagTable => 'Image::ExifTool::QuickTime::Meta',
482             Start => 4, # skip 4-byte version number header
483             },
484             },
485             meco => { #ISO14496-12:2015
486             Name => 'OtherMeta',
487             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::OtherMeta' },
488             },
489             free => [
490             {
491             Name => 'KodakFree',
492             # (found in Kodak M5370 MP4 videos)
493             Condition => '$$valPt =~ /^\0\0\0.Seri/s',
494             SubDirectory => { TagTable => 'Image::ExifTool::Kodak::Free' },
495             },{
496             Name => 'Pittasoft',
497             # (Pittasoft Blackview dashcam MP4 videos)
498             Condition => '$$valPt =~ /^\0\0..(cprt|sttm|ptnm|ptrh|thum|gps |3gf )/s',
499             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Pittasoft' },
500             },{
501             Name => 'ThumbnailImage',
502             # (DJI Zenmuse XT2 thermal camera)
503             Groups => { 2 => 'Preview' },
504             Condition => '$$valPt =~ /^.{4}mdat\xff\xd8\xff/s',
505             RawConv => q{
506             my $len = unpack('N', $val);
507             return undef if $len <= 8 or $len > length($val);
508             return substr($val, 8, $len-8);
509             },
510             Binary => 1,
511             },{
512             Unknown => 1,
513             Binary => 1,
514             },
515             # (also Samsung WB750 uncompressed thumbnail data starting with "SDIC\0")
516             ],
517             # fre1 - 4 bytes: "june" (Kodak PixPro SP360)
518             frea => {
519             Name => 'Kodak_frea',
520             SubDirectory => { TagTable => 'Image::ExifTool::Kodak::frea' },
521             },
522             skip => [
523             {
524             Name => 'CanonSkip',
525             Condition => '$$valPt =~ /^\0.{3}(CNDB|CNCV|CNMN|CNFV|CNTH|CNDM)/s',
526             SubDirectory => { TagTable => 'Image::ExifTool::Canon::Skip' },
527             },
528             {
529             Name => 'PreviewImage', # (found in DuDuBell M1 dashcam MOV files)
530             Groups => { 2 => 'Preview' },
531             Condition => '$$valPt =~ /^.{12}\xff\xd8\xff/',
532             Binary => 1,
533             RawConv => q{
534             my $len = Get32u(\$val, 8);
535             return undef unless length($val) >= $len + 12;
536             return substr($val, 12, $len);
537             },
538             },
539             {
540             Name => 'SkipInfo', # (found in 70mai Pro Plus+ MP4 videos)
541             # (look for something that looks like a QuickTime atom header)
542             Condition => '$$valPt =~ /^\0[\0-\x04]..[a-zA-Z ]{4}/s',
543             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SkipInfo' },
544             },
545             { Name => 'Skip', Unknown => 1, Binary => 1 },
546             ],
547             wide => { Unknown => 1, Binary => 1 },
548             ftyp => { #MP4
549             Name => 'FileType',
550             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::FileType' },
551             },
552             pnot => {
553             Name => 'Preview',
554             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Preview' },
555             },
556             PICT => {
557             Name => 'PreviewPICT',
558             Groups => { 2 => 'Preview' },
559             Binary => 1,
560             },
561             pict => { #8
562             Name => 'PreviewPICT',
563             Groups => { 2 => 'Preview' },
564             Binary => 1,
565             },
566             # (note that moov is present for an HEIF sequence)
567             moov => {
568             Name => 'Movie',
569             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Movie' },
570             },
571             moof => {
572             Name => 'MovieFragment',
573             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MovieFragment' },
574             },
575             # mfra - movie fragment random access: contains tfra (track fragment random access), and
576             # mfro (movie fragment random access offset) (ref 5)
577             mdat => { Name => 'MediaData', Unknown => 1, Binary => 1 },
578             'mdat-size' => {
579             Name => 'MediaDataSize',
580             RawConv => '$$self{MediaDataSize} = $val',
581             Notes => q{
582             not a real tag ID, this tag represents the size of the 'mdat' data in bytes
583             and is used in the AvgBitrate calculation
584             },
585             },
586             'mdat-offset' => {
587             Name => 'MediaDataOffset',
588             RawConv => '$$self{MediaDataOffset} = $val',
589             },
590             junk => { Unknown => 1, Binary => 1 }, #8
591             uuid => [
592             { #9 (MP4 files)
593             Name => 'XMP',
594             # *** this is where ExifTool writes XMP in MP4 videos (as per XMP spec) ***
595             Condition => '$$valPt=~/^\xbe\x7a\xcf\xcb\x97\xa9\x42\xe8\x9c\x71\x99\x94\x91\xe3\xaf\xac/',
596             WriteGroup => 'XMP', # (write main XMP tags here)
597             PreservePadding => 1,
598             SubDirectory => {
599             TagTable => 'Image::ExifTool::XMP::Main',
600             Start => 16,
601             },
602             },
603             { #11 (MP4 files)
604             Name => 'UUID-PROF',
605             Condition => '$$valPt=~/^PROF!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
606             SubDirectory => {
607             TagTable => 'Image::ExifTool::QuickTime::Profile',
608             Start => 24, # uid(16) + version(1) + flags(3) + count(4)
609             },
610             },
611             { #PH (Flip MP4 files)
612             Name => 'UUID-Flip',
613             Condition => '$$valPt=~/^\x4a\xb0\x3b\x0f\x61\x8d\x40\x75\x82\xb2\xd9\xfa\xce\xd3\x5f\xf5/',
614             SubDirectory => {
615             TagTable => 'Image::ExifTool::QuickTime::Flip',
616             Start => 16,
617             },
618             },
619             # "\x98\x7f\xa3\xdf\x2a\x85\x43\xc0\x8f\x8f\xd9\x7c\x47\x1e\x8e\xea" - unknown data in Flip videos
620             { #PH (Canon CR3)
621             Name => 'UUID-Canon2',
622             WriteLast => 1, # MUST come after mdat or DPP will drop mdat when writing!
623             Condition => '$$valPt=~/^\x21\x0f\x16\x87\x91\x49\x11\xe4\x81\x11\x00\x24\x21\x31\xfc\xe4/',
624             SubDirectory => {
625             TagTable => 'Image::ExifTool::Canon::uuid2',
626             Start => 16,
627             },
628             },
629             { # (ref https://github.com/JamesHeinrich/getID3/blob/master/getid3/module.audio-video.quicktime.php)
630             Name => 'SensorData', # sensor data for the 360Fly
631             Condition => '$$valPt=~/^\xef\xe1\x58\x9a\xbb\x77\x49\xef\x80\x95\x27\x75\x9e\xb1\xdc\x6f/ and $$self{OPTIONS}{ExtractEmbedded}',
632             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Tags360Fly' },
633             },
634             {
635             Name => 'SensorData',
636             Condition => '$$valPt=~/^\xef\xe1\x58\x9a\xbb\x77\x49\xef\x80\x95\x27\x75\x9e\xb1\xdc\x6f/',
637             Notes => 'raw 360Fly sensor data without ExtractEmbedded option',
638             RawConv => q{
639             $self->WarnOnce('Use the ExtractEmbedded option to decode timed SensorData',3);
640             return \$val;
641             },
642             },
643             { #PH (Canon CR3)
644             Name => 'PreviewImage',
645             Condition => '$$valPt=~/^\xea\xf4\x2b\x5e\x1c\x98\x4b\x88\xb9\xfb\xb7\xdc\x40\x6e\x4d\x16/',
646             Groups => { 2 => 'Preview' },
647             PreservePadding => 1,
648             # 0x00 - undef[16]: UUID
649             # 0x10 - int32u[2]: "0 1" (version and/or item count?)
650             # 0x18 - int32u: PRVW atom size
651             # 0x20 - int32u: 'PRVW'
652             # 0x30 - int32u: 0
653             # 0x34 - int16u: 1
654             # 0x36 - int16u: image width
655             # 0x38 - int16u: image height
656             # 0x3a - int16u: 1
657             # 0x3c - int32u: preview length
658             RawConv => '$val = substr($val, 0x30); $self->ValidateImage(\$val, $tag)',
659             },
660             { #8
661             Name => 'UUID-Unknown',
662             %unknownInfo,
663             },
664             ],
665             _htc => {
666             Name => 'HTCInfo',
667             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HTCInfo' },
668             },
669             udta => [{
670             Name => 'KenwoodData',
671             Condition => '$$valPt =~ /^VIDEOUUUUUUUUUUUUUUUUUUUUUU/',
672             SubDirectory => {
673             TagTable => 'Image::ExifTool::QuickTime::Stream',
674             ProcessProc => \&ProcessKenwood,
675             },
676             },{
677             Name => 'FLIRData',
678             SubDirectory => { TagTable => 'Image::ExifTool::FLIR::UserData' },
679             }],
680             thum => { #PH
681             Name => 'ThumbnailImage',
682             Groups => { 2 => 'Preview' },
683             Binary => 1,
684             },
685             'thm ' => { #PH (70mai A800)
686             Name => 'ThumbnailImage',
687             Groups => { 2 => 'Preview' },
688             Binary => 1,
689             },
690             ardt => { #PH
691             Name => 'ARDroneFile',
692             ValueConv => 'length($val) > 4 ? substr($val,4) : $val', # remove length
693             },
694             prrt => { #PH
695             Name => 'ARDroneTelemetry',
696             Notes => q{
697             telemetry information for each video frame: status1, status2, time, pitch,
698             roll, yaw, speed, altitude
699             },
700             ValueConv => q{
701             my $size = length $val;
702             return \$val if $size < 12 or not $$self{OPTIONS}{Binary};
703             my $len = Get16u(\$val, 2);
704             my $str = '';
705             SetByteOrder('II');
706             my $pos = 12;
707             while ($pos + $len <= $size) {
708             my $s1 = Get16u(\$val, $pos);
709             # s2: 7=take-off?, 3=moving, 4=hovering, 9=landing?, 2=landed
710             my $s2 = Get16u(\$val, $pos + 2);
711             $str .= "$s1 $s2";
712             my $num = int(($len-4)/4);
713             my ($i, $v);
714             for ($i=0; $i<$num; ++$i) {
715             my $pt = $pos + 4 + $i * 4;
716             if ($i > 0 && $i < 4) {
717             $v = GetFloat(\$val, $pt); # pitch/roll/yaw
718             } else {
719             $v = Get32u(\$val, $pt);
720             # convert time to sec, and speed(NC)/altitude to metres
721             $v /= 1000 if $i <= 5;
722             }
723             $str .= " $v";
724             }
725             $str .= "\n";
726             $pos += $len;
727             }
728             SetByteOrder('MM');
729             return \$str;
730             },
731             },
732             udat => { #PH (GPS NMEA-format log written by Datakam Player software)
733             Name => 'GPSLog',
734             Binary => 1, # (actually ASCII, but very lengthy)
735             Notes => 'parsed to extract GPS separately when ExtractEmbedded is used',
736             RawConv => q{
737             $val =~ s/\0+$//; # remove trailing nulls
738             if (length $val and $$self{OPTIONS}{ExtractEmbedded}) {
739             my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
740             Image::ExifTool::QuickTime::ProcessGPSLog($self, { DataPt => \$val }, $tagTbl);
741             }
742             return $val;
743             },
744             },
745             # meta - proprietary XML information written by some Flip cameras - PH
746             # beam - 16 bytes found in an iPhone video
747             IDIT => { #PH (written by DuDuBell M1, VSYS M6L dashcams)
748             Name => 'DateTimeOriginal',
749             Description => 'Date/Time Original',
750             Groups => { 2 => 'Time' },
751             Format => 'string', # (removes trailing "\0")
752             Shift => 'Time',
753             Writable => 1,
754             Permanent => 1,
755             DelValue => '0000-00-00T00:00:00+0000',
756             ValueConv => '$val=~tr/-/:/; $val',
757             ValueConvInv => '$val=~s/(\d+):(\d+):/$1-$2-/; $val',
758             PrintConv => '$self->ConvertDateTime($val)',
759             PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
760             },
761             gps0 => { #PH (DuDuBell M1, VSYS M6L)
762             Name => 'GPSTrack',
763             SubDirectory => {
764             TagTable => 'Image::ExifTool::QuickTime::Stream',
765             ProcessProc => \&Process_gps0,
766             },
767             },
768             gsen => { #PH (DuDuBell M1, VSYS M6L)
769             Name => 'GSensor',
770             SubDirectory => {
771             TagTable => 'Image::ExifTool::QuickTime::Stream',
772             ProcessProc => \&Process_gsen,
773             },
774             },
775             # gpsa - seen hex "01 20 00 00" (DuDuBell M1, VSYS M6L)
776             # gsea - 20 bytes hex "05 00's..." (DuDuBell M1) "05 08 02 01 ..." (VSYS M6L)
777             'GPS ' => { # GPS data written by 70mai dashcam (parsed in QuickTimeStream.pl)
778             Name => 'GPSDataList2',
779             Unknown => 1,
780             Binary => 1,
781             },
782             sefd => {
783             Name => 'SamsungTrailer',
784             SubDirectory => { TagTable => 'Image::ExifTool::Samsung::Trailer' },
785             },
786             # 'samn'? - seen in Vantrue N2S sample video
787             mpvd => {
788             Name => 'MotionPhotoVideo',
789             Notes => 'MP4-format video saved in Samsung motion-photo HEIC images.',
790             Binary => 1,
791             # note that this may be written and/or deleted, but can't currently be added back again
792             Writable => 1,
793             },
794             );
795              
796             # stuff seen in 'skip' atom (70mai Pro Plus+ MP4 videos)
797             %Image::ExifTool::QuickTime::SkipInfo = (
798             PROCESS_PROC => \&ProcessMOV,
799             GROUPS => { 2 => 'Video' },
800             'ver ' => 'Version',
801             # tima - int32u: seen 0x3c
802             thma => {
803             Name => 'ThumbnailImage',
804             Groups => { 2 => 'Preview' },
805             Binary => 1,
806             },
807             );
808              
809             # MPEG-4 'ftyp' atom
810             # (ref http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html)
811             %Image::ExifTool::QuickTime::FileType = (
812             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
813             GROUPS => { 2 => 'Video' },
814             FORMAT => 'int32u',
815             0 => {
816             Name => 'MajorBrand',
817             Format => 'undef[4]',
818             PrintConv => \%ftypLookup,
819             },
820             1 => {
821             Name => 'MinorVersion',
822             Format => 'undef[4]',
823             ValueConv => 'sprintf("%x.%x.%x", unpack("nCC", $val))',
824             },
825             2 => {
826             Name => 'CompatibleBrands',
827             Format => 'undef[$size-8]',
828             # ignore any entry with a null, and return others as a list
829             ValueConv => 'my @a=($val=~/.{4}/sg); @a=grep(!/\0/,@a); \@a',
830             },
831             );
832              
833             # proprietary HTC atom (HTC One MP4 video)
834             %Image::ExifTool::QuickTime::HTCInfo = (
835             PROCESS_PROC => \&ProcessMOV,
836             GROUPS => { 2 => 'Video' },
837             NOTES => 'Tags written by some HTC camera phones.',
838             slmt => {
839             Name => 'Unknown_slmt',
840             Unknown => 1,
841             Format => 'int32u', # (observed values: 4)
842             },
843             );
844              
845             # atoms used in QTIF files
846             %Image::ExifTool::QuickTime::ImageFile = (
847             PROCESS_PROC => \&ProcessMOV,
848             GROUPS => { 2 => 'Image' },
849             NOTES => 'Tags used in QTIF QuickTime Image Files.',
850             idsc => {
851             Name => 'ImageDescription',
852             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ImageDesc' },
853             },
854             idat => {
855             Name => 'ImageData',
856             Binary => 1,
857             },
858             iicc => {
859             Name => 'ICC_Profile',
860             SubDirectory => { TagTable => 'Image::ExifTool::ICC_Profile::Main' },
861             },
862             );
863              
864             # image description data block
865             %Image::ExifTool::QuickTime::ImageDesc = (
866             PROCESS_PROC => \&ProcessHybrid,
867             VARS => { ID_LABEL => 'ID/Index' },
868             GROUPS => { 2 => 'Image' },
869             FORMAT => 'int16u',
870             2 => {
871             Name => 'CompressorID',
872             Format => 'string[4]',
873             # not very useful since this isn't a complete list and name is given below
874             # # ref http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html
875             # PrintConv => {
876             # cvid => 'Cinepak',
877             # jpeg => 'JPEG',
878             # 'smc '=> 'Graphics',
879             # 'rle '=> 'Animation',
880             # rpza => 'Apple Video',
881             # kpcd => 'Kodak Photo CD',
882             # 'png '=> 'Portable Network Graphics',
883             # mjpa => 'Motion-JPEG (format A)',
884             # mjpb => 'Motion-JPEG (format B)',
885             # SVQ1 => 'Sorenson video, version 1',
886             # SVQ3 => 'Sorenson video, version 3',
887             # mp4v => 'MPEG-4 video',
888             # 'dvc '=> 'NTSC DV-25 video',
889             # dvcp => 'PAL DV-25 video',
890             # 'gif '=> 'Compuserve Graphics Interchange Format',
891             # h263 => 'H.263 video',
892             # tiff => 'Tagged Image File Format',
893             # 'raw '=> 'Uncompressed RGB',
894             # '2vuY'=> "Uncompressed Y'CbCr, 3x8-bit 4:2:2 (2vuY)",
895             # 'yuv2'=> "Uncompressed Y'CbCr, 3x8-bit 4:2:2 (yuv2)",
896             # v308 => "Uncompressed Y'CbCr, 8-bit 4:4:4",
897             # v408 => "Uncompressed Y'CbCr, 8-bit 4:4:4:4",
898             # v216 => "Uncompressed Y'CbCr, 10, 12, 14, or 16-bit 4:2:2",
899             # v410 => "Uncompressed Y'CbCr, 10-bit 4:4:4",
900             # v210 => "Uncompressed Y'CbCr, 10-bit 4:2:2",
901             # hvc1 => 'HEVC', #PH
902             # },
903             },
904             10 => {
905             Name => 'VendorID',
906             Format => 'string[4]',
907             RawConv => 'length $val ? $val : undef',
908             PrintConv => \%vendorID,
909             SeparateTable => 'VendorID',
910             },
911             # 14 - ("Quality" in QuickTime docs) ??
912             16 => 'SourceImageWidth',
913             17 => 'SourceImageHeight',
914             18 => { Name => 'XResolution', Format => 'fixed32u' },
915             20 => { Name => 'YResolution', Format => 'fixed32u' },
916             # 24 => 'FrameCount', # always 1 (what good is this?)
917             25 => {
918             Name => 'CompressorName',
919             Format => 'string[32]',
920             # (sometimes this is a Pascal string, and sometimes it is a C string)
921             RawConv => q{
922             $val=substr($val,1,ord($1)) if $val=~/^([\0-\x1f])/ and ord($1)
923             length $val ? $val : undef;
924             },
925             },
926             41 => 'BitDepth',
927             #
928             # Observed offsets for child atoms of various CompressorID types:
929             #
930             # CompressorID Offset Child atoms
931             # ----------- ------ ----------------
932             # avc1 86 avcC, btrt, colr, pasp, fiel, clap, svcC
933             # mp4v 86 esds, pasp
934             # s263 86 d263
935             #
936             btrt => {
937             Name => 'BitrateInfo',
938             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Bitrate' },
939             },
940             # Reference for fiel, colr, pasp, clap:
941             # https://developer.apple.com/library/mac/technotes/tn2162/_index.html#//apple_ref/doc/uid/DTS40013070-CH1-TNTAG9
942             fiel => {
943             Name => 'VideoFieldOrder',
944             ValueConv => 'join(" ", unpack("C*",$val))',
945             PrintConv => [{
946             1 => 'Progressive',
947             2 => '2:1 Interlaced',
948             }],
949             },
950             colr => {
951             Name => 'ColorRepresentation',
952             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ColorRep' },
953             },
954             pasp => {
955             Name => 'PixelAspectRatio',
956             ValueConv => 'join(":", unpack("N*",$val))',
957             },
958             clap => {
959             Name => 'CleanAperture',
960             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::CleanAperture' },
961             },
962             avcC => {
963             # (see http://thompsonng.blogspot.ca/2010/11/mp4-file-format-part-2.html)
964             Name => 'AVCConfiguration',
965             Unknown => 1,
966             Binary => 1,
967             },
968             JPEG => { # (found in CR3 images; used as a flag to identify JpgFromRaw 'vide' stream)
969             Name => 'JPEGInfo',
970             # (4 bytes all zero)
971             Unknown => 1,
972             Binary => 1,
973             },
974             # hvcC - HEVC configuration
975             # svcC - 7 bytes: 00 00 00 00 ff e0 00
976             # esds - elementary stream descriptor
977             # d263
978             gama => { Name => 'Gamma', Format => 'fixed32u' },
979             # mjqt - default quantization table for MJPEG
980             # mjht - default Huffman table for MJPEG
981             # csgm ? (seen in hevc video)
982             CMP1 => { # Canon CR3
983             Name => 'CMP1',
984             SubDirectory => { TagTable => 'Image::ExifTool::Canon::CMP1' },
985             },
986             CDI1 => { # Canon CR3
987             Name => 'CDI1',
988             SubDirectory => {
989             TagTable => 'Image::ExifTool::Canon::CDI1',
990             Start => 4,
991             },
992             },
993             # JPEG - 4 bytes all 0 (Canon CR3)
994             # free - (Canon CR3)
995             #
996             # spherical video v2 stuff (untested)
997             #
998             st3d => {
999             Name => 'Stereoscopic3D',
1000             Format => 'int8u',
1001             ValueConv => '$val =~ s/.* //; $val', # (remove leading version/flags bytes?)
1002             PrintConv => {
1003             0 => 'Monoscopic',
1004             1 => 'Stereoscopic Top-Bottom',
1005             2 => 'Stereoscopic Left-Right',
1006             3 => 'Stereoscopic Stereo-Custom', # (provisional in spec as of 2017-10-10)
1007             },
1008             },
1009             sv3d => {
1010             Name => 'SphericalVideo',
1011             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::sv3d' },
1012             },
1013             );
1014              
1015             # 'sv3d' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1016             %Image::ExifTool::QuickTime::sv3d = (
1017             PROCESS_PROC => \&ProcessMOV,
1018             GROUPS => { 2 => 'Video' },
1019             NOTES => q{
1020             Tags defined by the Spherical Video V2 specification. See
1021             L
1022             for the specification.
1023             },
1024             svhd => {
1025             Name => 'MetadataSource',
1026             Format => 'undef',
1027             ValueConv => '$val=~tr/\0//d; $val', # (remove version/flags? and terminator?)
1028             },
1029             proj => {
1030             Name => 'Projection',
1031             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::proj' },
1032             },
1033             );
1034              
1035             # 'proj' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1036             %Image::ExifTool::QuickTime::proj = (
1037             PROCESS_PROC => \&ProcessMOV,
1038             GROUPS => { 2 => 'Video' },
1039             prhd => {
1040             Name => 'ProjectionHeader',
1041             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::prhd' },
1042             },
1043             cbmp => {
1044             Name => 'CubemapProj',
1045             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::cbmp' },
1046             },
1047             equi => {
1048             Name => 'EquirectangularProj',
1049             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::equi' },
1050             },
1051             # mshp - MeshProjection (P.I.T.A. to decode, for not much reward, see ref)
1052             );
1053              
1054             # 'prhd' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1055             %Image::ExifTool::QuickTime::prhd = (
1056             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1057             GROUPS => { 2 => 'Video' },
1058             FORMAT => 'fixed32s',
1059             # 0 - version (high 8 bits) / flags (low 24 bits)
1060             1 => 'PoseYawDegrees',
1061             2 => 'PosePitchDegrees',
1062             3 => 'PoseRollDegrees',
1063             );
1064              
1065             # 'cbmp' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1066             %Image::ExifTool::QuickTime::cbmp = (
1067             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1068             GROUPS => { 2 => 'Video' },
1069             FORMAT => 'int32u',
1070             # 0 - version (high 8 bits) / flags (low 24 bits)
1071             1 => 'Layout',
1072             2 => 'Padding',
1073             );
1074              
1075             # 'equi' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1076             %Image::ExifTool::QuickTime::equi = (
1077             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1078             GROUPS => { 2 => 'Video' },
1079             FORMAT => 'int32u', # (actually 0.32 fixed point)
1080             # 0 - version (high 8 bits) / flags (low 24 bits)
1081             1 => { Name => 'ProjectionBoundsTop', ValueConv => '$val / 4294967296' },
1082             2 => { Name => 'ProjectionBoundsBottom',ValueConv => '$val / 4294967296' },
1083             3 => { Name => 'ProjectionBoundsLeft', ValueConv => '$val / 4294967296' },
1084             4 => { Name => 'ProjectionBoundsRight', ValueConv => '$val / 4294967296' },
1085             );
1086              
1087             # 'btrt' atom information (ref http://lists.freedesktop.org/archives/gstreamer-commits/2011-October/054459.html)
1088             %Image::ExifTool::QuickTime::Bitrate = (
1089             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1090             GROUPS => { 2 => 'Video' },
1091             FORMAT => 'int32u',
1092             PRIORITY => 0, # often filled with zeros
1093             0 => 'BufferSize',
1094             1 => 'MaxBitrate',
1095             2 => 'AverageBitrate',
1096             );
1097              
1098             # 'clap' atom information (ref https://developer.apple.com/library/mac/technotes/tn2162/_index.html#//apple_ref/doc/uid/DTS40013070-CH1-TNTAG9)
1099             %Image::ExifTool::QuickTime::CleanAperture = (
1100             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1101             GROUPS => { 2 => 'Video' },
1102             FORMAT => 'rational64s',
1103             0 => 'CleanApertureWidth',
1104             1 => 'CleanApertureHeight',
1105             2 => 'CleanApertureOffsetX',
1106             3 => 'CleanApertureOffsetY',
1107             );
1108              
1109             # preview data block
1110             %Image::ExifTool::QuickTime::Preview = (
1111             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1112             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
1113             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
1114             GROUPS => { 2 => 'Image' },
1115             FORMAT => 'int16u',
1116             0 => {
1117             Name => 'PreviewDate',
1118             Format => 'int32u',
1119             Groups => { 2 => 'Time' },
1120             %timeInfo,
1121             },
1122             2 => 'PreviewVersion',
1123             3 => {
1124             Name => 'PreviewAtomType',
1125             Format => 'string[4]',
1126             },
1127             5 => 'PreviewAtomIndex',
1128             );
1129              
1130             # movie atoms
1131             %Image::ExifTool::QuickTime::Movie = (
1132             PROCESS_PROC => \&ProcessMOV,
1133             WRITE_PROC => \&WriteQuickTime,
1134             GROUPS => { 2 => 'Video' },
1135             mvhd => {
1136             Name => 'MovieHeader',
1137             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MovieHeader' },
1138             },
1139             trak => {
1140             Name => 'Track',
1141             CanCreate => 0, # don't create this atom
1142             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Track' },
1143             },
1144             udta => {
1145             Name => 'UserData',
1146             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::UserData' },
1147             },
1148             meta => { # 'meta' is found here in my EX-F1 MOV sample - PH
1149             Name => 'Meta',
1150             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1151             },
1152             iods => {
1153             Name => 'InitialObjectDescriptor',
1154             Flags => ['Binary','Unknown'],
1155             },
1156             uuid => [
1157             { #11 (MP4 files) (also found in QuickTime::Track)
1158             Name => 'UUID-USMT',
1159             Condition => '$$valPt=~/^USMT!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
1160             SubDirectory => {
1161             TagTable => 'Image::ExifTool::QuickTime::UserMedia',
1162             Start => 16,
1163             },
1164             },
1165             { #PH (Canon SX280)
1166             Name => 'UUID-Canon',
1167             Condition => '$$valPt=~/^\x85\xc0\xb6\x87\x82\x0f\x11\xe0\x81\x11\xf4\xce\x46\x2b\x6a\x48/',
1168             SubDirectory => {
1169             TagTable => 'Image::ExifTool::Canon::uuid',
1170             Start => 16,
1171             },
1172             },
1173             {
1174             Name => 'GarminGPS',
1175             Condition => q{
1176             $$valPt=~/^\x9b\x63\x0f\x8d\x63\x74\x40\xec\x82\x04\xbc\x5f\xf5\x09\x17\x28/ and
1177             $$self{OPTIONS}{ExtractEmbedded}
1178             },
1179             SubDirectory => {
1180             TagTable => 'Image::ExifTool::QuickTime::Stream',
1181             ProcessProc => \&ProcessGarminGPS,
1182             },
1183             },
1184             {
1185             Name => 'GarminGPS',
1186             Condition => '$$valPt=~/^\x9b\x63\x0f\x8d\x63\x74\x40\xec\x82\x04\xbc\x5f\xf5\x09\x17\x28/',
1187             Notes => 'Garmin GPS sensor data',
1188             RawConv => q{
1189             $self->WarnOnce('Use the ExtractEmbedded option to decode timed Garmin GPS',3);
1190             return \$val;
1191             },
1192             },
1193             {
1194             Name => 'UUID-Unknown',
1195             %unknownInfo,
1196             },
1197             ],
1198             cmov => {
1199             Name => 'CompressedMovie',
1200             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::CMovie' },
1201             },
1202             htka => { # (written by HTC One M8 in slow-motion 1280x720 video - PH)
1203             Name => 'HTCTrack',
1204             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Track' },
1205             },
1206             'gps ' => { # GPS data written by Novatek cameras (parsed in QuickTimeStream.pl)
1207             Name => 'GPSDataList',
1208             Unknown => 1,
1209             Binary => 1,
1210             },
1211             meco => { #ISO14496-12:2015
1212             Name => 'OtherMeta',
1213             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::OtherMeta' },
1214             },
1215             # prfl - Profile (ref 12)
1216             # clip - clipping --> contains crgn (clip region) (ref 12)
1217             # mvex - movie extends --> contains mehd (movie extends header), trex (track extends) (ref 14)
1218             # ICAT - 4 bytes: "6350" (Nikon CoolPix S6900), "6500" (Panasonic FT7)
1219             );
1220              
1221             # (ref CFFMediaFormat-2_1.pdf)
1222             %Image::ExifTool::QuickTime::MovieFragment = (
1223             PROCESS_PROC => \&ProcessMOV,
1224             WRITE_PROC => \&WriteQuickTime,
1225             GROUPS => { 2 => 'Video' },
1226             mfhd => {
1227             Name => 'MovieFragmentHeader',
1228             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MovieFragHdr' },
1229             },
1230             traf => {
1231             Name => 'TrackFragment',
1232             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackFragment' },
1233             },
1234             meta => { #ISO14496-12:2015
1235             Name => 'Meta',
1236             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1237             },
1238             );
1239              
1240             # (ref CFFMediaFormat-2_1.pdf)
1241             %Image::ExifTool::QuickTime::MovieFragHdr = (
1242             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1243             GROUPS => { 2 => 'Video' },
1244             FORMAT => 'int32u',
1245             1 => 'MovieFragmentSequence',
1246             );
1247              
1248             # (ref CFFMediaFormat-2_1.pdf)
1249             %Image::ExifTool::QuickTime::TrackFragment = (
1250             PROCESS_PROC => \&ProcessMOV,
1251             WRITE_PROC => \&WriteQuickTime,
1252             GROUPS => { 2 => 'Video' },
1253             meta => { #ISO14496-12:2015
1254             Name => 'Meta',
1255             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1256             },
1257             # tfhd - track fragment header
1258             # edts - edits --> contains elst (edit list) (ref PH)
1259             # tfdt - track fragment base media decode time
1260             # trik - trick play box
1261             # trun - track fragment run box
1262             # avcn - AVC NAL unit storage box
1263             # secn - sample encryption box
1264             # saio - sample auxiliary information offsets box
1265             # sbgp - sample to group box
1266             # sgpd - sample group description box
1267             # sdtp - independent and disposable samples (ref 5)
1268             # subs - sub-sample information (ref 5)
1269             );
1270              
1271             # movie header data block
1272             %Image::ExifTool::QuickTime::MovieHeader = (
1273             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1274             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
1275             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
1276             GROUPS => { 2 => 'Video' },
1277             FORMAT => 'int32u',
1278             DATAMEMBER => [ 0, 1, 2, 3, 4 ],
1279             0 => {
1280             Name => 'MovieHeaderVersion',
1281             Format => 'int8u',
1282             RawConv => '$$self{MovieHeaderVersion} = $val',
1283             },
1284             1 => {
1285             Name => 'CreateDate',
1286             Groups => { 2 => 'Time' },
1287             %timeInfo,
1288             RawConv => q{
1289             my $offset = (66 * 365 + 17) * 24 * 3600;
1290             if ($val >= $offset or $$self{OPTIONS}{QuickTimeUTC}) {
1291             $val -= $offset;
1292             } elsif ($val and not $$self{IsWriting}) {
1293             $self->WarnOnce('Patched incorrect time zero for QuickTime date/time tag',1);
1294             }
1295             return $$self{CreateDate} = $val;
1296             },
1297             # this is int64u if MovieHeaderVersion == 1 (ref 13)
1298             Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
1299             },
1300             2 => {
1301             Name => 'ModifyDate',
1302             Groups => { 2 => 'Time' },
1303             %timeInfo,
1304             # this is int64u if MovieHeaderVersion == 1 (ref 13)
1305             Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
1306             },
1307             3 => {
1308             Name => 'TimeScale',
1309             RawConv => '$$self{TimeScale} = $val',
1310             },
1311             4 => {
1312             Name => 'Duration',
1313             %durationInfo,
1314             # this is int64u if MovieHeaderVersion == 1 (ref 13)
1315             Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
1316             },
1317             5 => {
1318             Name => 'PreferredRate',
1319             ValueConv => '$val / 0x10000',
1320             },
1321             6 => {
1322             Name => 'PreferredVolume',
1323             Format => 'int16u',
1324             ValueConv => '$val / 256',
1325             PrintConv => 'sprintf("%.2f%%", $val * 100)',
1326             },
1327             9 => {
1328             Name => 'MatrixStructure',
1329             Format => 'fixed32s[9]',
1330             # (the right column is fixed 2.30 instead of 16.16)
1331             ValueConv => q{
1332             my @a = split ' ',$val;
1333             $_ /= 0x4000 foreach @a[2,5,8];
1334             return "@a";
1335             },
1336             },
1337             18 => { Name => 'PreviewTime', %durationInfo },
1338             19 => { Name => 'PreviewDuration', %durationInfo },
1339             20 => { Name => 'PosterTime', %durationInfo },
1340             21 => { Name => 'SelectionTime', %durationInfo },
1341             22 => { Name => 'SelectionDuration',%durationInfo },
1342             23 => { Name => 'CurrentTime', %durationInfo },
1343             24 => 'NextTrackID',
1344             );
1345              
1346             # track atoms
1347             %Image::ExifTool::QuickTime::Track = (
1348             PROCESS_PROC => \&ProcessMOV,
1349             WRITE_PROC => \&WriteQuickTime,
1350             GROUPS => { 1 => 'Track#', 2 => 'Video' },
1351             tkhd => {
1352             Name => 'TrackHeader',
1353             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackHeader' },
1354             },
1355             udta => {
1356             Name => 'UserData',
1357             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::UserData' },
1358             },
1359             mdia => { #MP4
1360             Name => 'Media',
1361             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Media' },
1362             },
1363             meta => { #PH (MOV)
1364             Name => 'Meta',
1365             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1366             },
1367             tref => {
1368             Name => 'TrackRef',
1369             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackRef' },
1370             },
1371             tapt => {
1372             Name => 'TrackAperture',
1373             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackAperture' },
1374             },
1375             uuid => [
1376             { #11 (MP4 files) (also found in QuickTime::Movie)
1377             Name => 'UUID-USMT',
1378             Condition => '$$valPt=~/^USMT!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
1379             SubDirectory => {
1380             TagTable => 'Image::ExifTool::QuickTime::UserMedia',
1381             Start => 16,
1382             },
1383             },
1384             { #https://github.com/google/spatial-media/blob/master/docs/spherical-video-rfc.md
1385             Name => 'SphericalVideoXML',
1386             # (this tag is readable/writable as a block through the Extra SphericalVideoXML tags)
1387             Condition => '$$valPt=~/^\xff\xcc\x82\x63\xf8\x55\x4a\x93\x88\x14\x58\x7a\x02\x52\x1f\xdd/',
1388             WriteGroup => 'GSpherical', # write only GSpherical XMP tags here
1389             HandlerType => 'vide', # only write in video tracks
1390             SubDirectory => {
1391             TagTable => 'Image::ExifTool::XMP::Main',
1392             Start => 16,
1393             ProcessProc => 'Image::ExifTool::XMP::ProcessGSpherical',
1394             WriteProc => 'Image::ExifTool::XMP::WriteGSpherical',
1395             },
1396             },
1397             {
1398             Name => 'UUID-Unknown',
1399             %unknownInfo,
1400             },
1401             ],
1402             meco => { #ISO14492-12:2015 pg 83
1403             Name => 'OtherMeta',
1404             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::OtherMeta' },
1405             },
1406             # edts - edits --> contains elst (edit list)
1407             # clip - clipping --> contains crgn (clip region)
1408             # matt - track matt --> contains kmat (compressed matt)
1409             # load - track loading settings
1410             # imap - track input map --> contains ' in' --> contains ' ty', obid
1411             # prfl - Profile (ref 12)
1412             );
1413              
1414             # track header data block
1415             %Image::ExifTool::QuickTime::TrackHeader = (
1416             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1417             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
1418             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
1419             GROUPS => { 1 => 'Track#', 2 => 'Video' },
1420             FORMAT => 'int32u',
1421             DATAMEMBER => [ 0, 1, 2, 5, 7 ],
1422             0 => {
1423             Name => 'TrackHeaderVersion',
1424             Format => 'int8u',
1425             Priority => 0,
1426             RawConv => '$$self{TrackHeaderVersion} = $val',
1427             },
1428             1 => {
1429             Name => 'TrackCreateDate',
1430             Priority => 0,
1431             Groups => { 2 => 'Time' },
1432             %timeInfo,
1433             # this is int64u if TrackHeaderVersion == 1 (ref 13)
1434             Hook => '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
1435             },
1436             2 => {
1437             Name => 'TrackModifyDate',
1438             Priority => 0,
1439             Groups => { 2 => 'Time' },
1440             %timeInfo,
1441             # this is int64u if TrackHeaderVersion == 1 (ref 13)
1442             Hook => '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
1443             },
1444             3 => {
1445             Name => 'TrackID',
1446             Priority => 0,
1447             },
1448             5 => {
1449             Name => 'TrackDuration',
1450             Priority => 0,
1451             %durationInfo,
1452             # this is int64u if TrackHeaderVersion == 1 (ref 13)
1453             Hook => '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
1454             },
1455             7 => { # (used only for writing MatrixStructure)
1456             Name => 'ImageSizeLookahead',
1457             Hidden => 1,
1458             Format => 'int32u[14]',
1459             RawConv => '$$self{ImageSizeLookahead} = $val; undef',
1460             },
1461             8 => {
1462             Name => 'TrackLayer',
1463             Format => 'int16u',
1464             Priority => 0,
1465             },
1466             9 => {
1467             Name => 'TrackVolume',
1468             Format => 'int16u',
1469             Priority => 0,
1470             ValueConv => '$val / 256',
1471             PrintConv => 'sprintf("%.2f%%", $val * 100)',
1472             },
1473             10 => {
1474             Name => 'MatrixStructure',
1475             Format => 'fixed32s[9]',
1476             Notes => 'writable for the video track via the Composite Rotation tag',
1477             Writable => 1,
1478             Protected => 1,
1479             Permanent => 1,
1480             # only set rotation if image size is non-zero
1481             RawConvInv => \&GetMatrixStructure,
1482             # (the right column is fixed 2.30 instead of 16.16)
1483             ValueConv => q{
1484             my @a = split ' ',$val;
1485             $_ /= 0x4000 foreach @a[2,5,8];
1486             return "@a";
1487             },
1488             ValueConvInv => q{
1489             my @a = split ' ',$val;
1490             $_ *= 0x4000 foreach @a[2,5,8];
1491             return "@a";
1492             },
1493             },
1494             19 => {
1495             Name => 'ImageWidth',
1496             Priority => 0,
1497             RawConv => \&FixWrongFormat,
1498             },
1499             20 => {
1500             Name => 'ImageHeight',
1501             Priority => 0,
1502             RawConv => \&FixWrongFormat,
1503             },
1504             );
1505              
1506             # user data atoms
1507             %Image::ExifTool::QuickTime::UserData = (
1508             PROCESS_PROC => \&ProcessMOV,
1509             WRITE_PROC => \&WriteQuickTime,
1510             CHECK_PROC => \&CheckQTValue,
1511             GROUPS => { 1 => 'UserData', 2 => 'Video' },
1512             WRITABLE => 1,
1513             PREFERRED => 1, # (preferred over Keys tags when writing)
1514             FORMAT => 'string',
1515             WRITE_GROUP => 'UserData',
1516             LANG_INFO => \&GetLangInfo,
1517             NOTES => q{
1518             Tag ID's beginning with the copyright symbol (hex 0xa9) are multi-language
1519             text. Alternate language tags are accessed by adding a dash followed by a
1520             3-character ISO 639-2 language code to the tag name. ExifTool will extract
1521             any multi-language user data tags found, even if they aren't in this table.
1522             Note when creating new tags,
1523             L tags are
1524             preferred over these, so to create the tag when a same-named ItemList tag
1525             exists, either "UserData" must be specified (eg. C<-UserData:Artist=Monet>
1526             on the command line), or the PREFERRED level must be changed via
1527             L.
1528             },
1529             "\xa9cpy" => { Name => 'Copyright', Groups => { 2 => 'Author' } },
1530             "\xa9day" => {
1531             Name => 'ContentCreateDate',
1532             Groups => { 2 => 'Time' },
1533             Shift => 'Time',
1534             # handle values in the form "2010-02-12T13:27:14-0800" (written by Apple iPhone)
1535             ValueConv => q{
1536             require Image::ExifTool::XMP;
1537             $val = Image::ExifTool::XMP::ConvertXMPDate($val);
1538             $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
1539             return $val;
1540             },
1541             ValueConvInv => q{
1542             require Image::ExifTool::XMP;
1543             my $tmp = Image::ExifTool::XMP::FormatXMPDate($val);
1544             ($val = $tmp) =~ s/([-+]\d{2}):(\d{2})$/$1$2/ if defined $tmp; # remove time zone colon
1545             return $val;
1546             },
1547             PrintConv => '$self->ConvertDateTime($val)',
1548             PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
1549             },
1550             "\xa9ART" => 'Artist', #PH (iTunes 8.0.2)
1551             "\xa9alb" => 'Album', #PH (iTunes 8.0.2)
1552             "\xa9arg" => 'Arranger', #12
1553             "\xa9ark" => 'ArrangerKeywords', #12
1554             "\xa9cmt" => 'Comment', #PH (iTunes 8.0.2)
1555             "\xa9cok" => 'ComposerKeywords', #12
1556             "\xa9com" => 'Composer', #12
1557             "\xa9dir" => 'Director', #12
1558             "\xa9ed1" => 'Edit1',
1559             "\xa9ed2" => 'Edit2',
1560             "\xa9ed3" => 'Edit3',
1561             "\xa9ed4" => 'Edit4',
1562             "\xa9ed5" => 'Edit5',
1563             "\xa9ed6" => 'Edit6',
1564             "\xa9ed7" => 'Edit7',
1565             "\xa9ed8" => 'Edit8',
1566             "\xa9ed9" => 'Edit9',
1567             "\xa9fmt" => 'Format',
1568             "\xa9gen" => 'Genre', #PH (iTunes 8.0.2)
1569             "\xa9grp" => 'Grouping', #PH (NC)
1570             "\xa9inf" => 'Information',
1571             "\xa9isr" => 'ISRCCode', #12
1572             "\xa9lab" => 'RecordLabelName', #12
1573             "\xa9lal" => 'RecordLabelURL', #12
1574             "\xa9lyr" => 'Lyrics', #PH (NC)
1575             "\xa9mak" => 'Make', #12
1576             "\xa9mal" => 'MakerURL', #12
1577             "\xa9mod" => 'Model', #PH
1578             "\xa9nam" => 'Title', #12
1579             "\xa9pdk" => 'ProducerKeywords', #12
1580             "\xa9phg" => 'RecordingCopyright', #12
1581             "\xa9prd" => 'Producer',
1582             "\xa9prf" => 'Performers',
1583             "\xa9prk" => 'PerformerKeywords', #12
1584             "\xa9prl" => 'PerformerURL',
1585             "\xa9req" => 'Requirements',
1586             "\xa9snk" => 'SubtitleKeywords', #12
1587             "\xa9snm" => 'Subtitle', #12
1588             "\xa9src" => 'SourceCredits', #12
1589             "\xa9swf" => 'SongWriter', #12
1590             "\xa9swk" => 'SongWriterKeywords', #12
1591             "\xa9swr" => 'SoftwareVersion', #12
1592             "\xa9too" => 'Encoder', #PH (NC)
1593             "\xa9trk" => 'Track', #PH (NC)
1594             "\xa9wrt" => { Name => 'Composer', Avoid => 1 }, # ("\xa9com" is preferred in UserData)
1595             "\xa9xyz" => { #PH (iPhone 3GS)
1596             Name => 'GPSCoordinates',
1597             Groups => { 2 => 'Location' },
1598             ValueConv => \&ConvertISO6709,
1599             ValueConvInv => \&ConvInvISO6709,
1600             PrintConv => \&PrintGPSCoordinates,
1601             PrintConvInv => \&PrintInvGPSCoordinates,
1602             },
1603             # \xa9 tags written by DJI Phantom 3: (ref PH)
1604             "\xa9xsp" => 'SpeedX', #PH (guess)
1605             "\xa9ysp" => 'SpeedY', #PH (guess)
1606             "\xa9zsp" => 'SpeedZ', #PH (guess)
1607             "\xa9fpt" => 'Pitch', #PH
1608             "\xa9fyw" => 'Yaw', #PH
1609             "\xa9frl" => 'Roll', #PH
1610             "\xa9gpt" => 'CameraPitch', #PH
1611             "\xa9gyw" => 'CameraYaw', #PH
1612             "\xa9grl" => 'CameraRoll', #PH
1613             "\xa9enc" => 'EncoderID', #PH (forum9271)
1614             # and the following entries don't have the proper 4-byte header for \xa9 tags:
1615             "\xa9dji" => { Name => 'UserData_dji', Format => 'undef', Binary => 1, Unknown => 1, Hidden => 1 },
1616             "\xa9res" => { Name => 'UserData_res', Format => 'undef', Binary => 1, Unknown => 1, Hidden => 1 },
1617             "\xa9uid" => { Name => 'UserData_uid', Format => 'undef', Binary => 1, Unknown => 1, Hidden => 1 },
1618             "\xa9mdl" => {
1619             Name => 'Model',
1620             Notes => 'non-standard-format DJI tag',
1621             Format => 'string',
1622             Avoid => 1,
1623             },
1624             # end DJI tags
1625             name => 'Name',
1626             WLOC => {
1627             Name => 'WindowLocation',
1628             Format => 'int16u',
1629             },
1630             LOOP => {
1631             Name => 'LoopStyle',
1632             Format => 'int32u',
1633             PrintConv => {
1634             1 => 'Normal',
1635             2 => 'Palindromic',
1636             },
1637             },
1638             SelO => {
1639             Name => 'PlaySelection',
1640             Format => 'int8u',
1641             },
1642             AllF => {
1643             Name => 'PlayAllFrames',
1644             Format => 'int8u',
1645             },
1646             meta => {
1647             Name => 'Meta',
1648             SubDirectory => {
1649             TagTable => 'Image::ExifTool::QuickTime::Meta',
1650             Start => 4, # must skip 4-byte version number header
1651             },
1652             },
1653             'ptv '=> {
1654             Name => 'PrintToVideo',
1655             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Video' },
1656             },
1657             hnti => {
1658             Name => 'HintInfo',
1659             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HintInfo' },
1660             },
1661             hinf => {
1662             Name => 'HintTrackInfo',
1663             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HintTrackInfo' },
1664             },
1665             hinv => 'HintVersion', #PH (guess)
1666             XMP_ => { #PH (Adobe CS3 Bridge)
1667             Name => 'XMP',
1668             WriteGroup => 'XMP', # (write main tags here)
1669             # *** this is where ExifTool writes XMP in MOV videos (as per XMP spec) ***
1670             SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
1671             },
1672             # the following are 3gp tags, references:
1673             # http://atomicparsley.sourceforge.net
1674             # http://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_25/Docs/
1675             # (note that all %langText3gp tags are Avoid => 1)
1676             cprt => { Name => 'Copyright', %langText3gp, Groups => { 2 => 'Author' } },
1677             auth => { Name => 'Author', %langText3gp, Groups => { 2 => 'Author' } },
1678             titl => { Name => 'Title', %langText3gp },
1679             dscp => { Name => 'Description',%langText3gp },
1680             perf => { Name => 'Performer', %langText3gp },
1681             gnre => { Name => 'Genre', %langText3gp },
1682             albm => { Name => 'Album', %langText3gp },
1683             coll => { Name => 'CollectionName', %langText3gp }, #17
1684             rtng => {
1685             Name => 'Rating',
1686             Writable => 'undef',
1687             Avoid => 1,
1688             # (4-byte flags, 4-char entity, 4-char criteria, 2-byte lang, string)
1689             IText => 14, # (14 bytes before string)
1690             Notes => 'string in the form "Entity=XXXX Criteria=XXXX XXXXX", used in 3gp videos',
1691             ValueConv => '$val=~s/^(.{4})(.{4})/Entity=$1 Criteria=$2 /i; $val',
1692             ValueConvInv => '$val=~s/Entity=(.{4}) Criteria=(.{4}) ?/$1$2/i; $val',
1693             },
1694             clsf => {
1695             Name => 'Classification',
1696             Writable => 'undef',
1697             Avoid => 1,
1698             # (4-byte flags, 4-char entity, 2-byte index, 2-byte lang, string)
1699             IText => 12,
1700             Notes => 'string in the form "Entity=XXXX Index=### XXXXX", used in 3gp videos',
1701             ValueConv => '$val=~s/^(.{4})(.{2})/"Entity=$1 Index=".unpack("n",$2)." "/ie; $val',
1702             ValueConvInv => '$val=~s/Entity=(.{4}) Index=(\d+) ?/$1.pack("n",$2)/ie; $val',
1703             },
1704             kywd => {
1705             Name => 'Keywords',
1706             # (4 byte flags, 2-byte lang, 1-byte count, count x pascal strings, ref 17)
1707             # (but I have also seen a simple string written by iPhone, so don't make writable yet)
1708             Notes => "not writable because Apple doesn't follow the 3gp specification",
1709             RawConv => q{
1710             my $sep = $self->Options('ListSep');
1711             return join($sep, split /\0+/, $val) unless $val =~ /^\0/; # (iPhone)
1712             return '' unless length $val >= 7;
1713             my $lang = Image::ExifTool::QuickTime::UnpackLang(Get16u(\$val, 4));
1714             $lang = $lang ? "($lang) " : '';
1715             my $num = Get8u(\$val, 6);
1716             my ($i, @vals);
1717             my $pos = 7;
1718             for ($i=0; $i<$num; ++$i) {
1719             last if $pos >= length $val;
1720             my $len = Get8u(\$val, $pos++);
1721             last if $pos + $len > length $val;
1722             my $v = substr($val, $pos, $len);
1723             $v = $self->Decode($v, 'UCS2') if $v =~ /^\xfe\xff/;
1724             push @vals, $v;
1725             $pos += $len;
1726             }
1727             return $lang . join($sep, @vals);
1728             },
1729             },
1730             loci => {
1731             Name => 'LocationInformation',
1732             Groups => { 2 => 'Location' },
1733             Writable => 'undef',
1734             IText => 6,
1735             Avoid => 1,
1736             NoDecode => 1, # (we'll decode the data ourself)
1737             Notes => q{
1738             string in the form "XXXXX Role=XXX Lat=XXX Lon=XXX Alt=XXX Body=XXX
1739             Notes=XXX", used in 3gp videos
1740             },
1741             # (4-byte flags, 2-byte lang, location string, 1-byte role, 4-byte fixed longitude,
1742             # 4-byte fixed latitude, 4-byte fixed altitude, body string, notes string)
1743             RawConv => q{
1744             my $str;
1745             if ($val =~ /^\xfe\xff/) {
1746             $val =~ s/^(\xfe\xff(.{2})*?)\0\0//s or return '';
1747             $str = $self->Decode($1, 'UCS2');
1748             } else {
1749             $val =~ s/^(.*?)\0//s or return '';
1750             $str = $self->Decode($1, 'UTF8');
1751             }
1752             $str = '(none)' unless length $str;
1753             return '' if length $val < 13;
1754             my $role = Get8u(\$val, 0);
1755             my $lon = GetFixed32s(\$val, 1);
1756             my $lat = GetFixed32s(\$val, 5);
1757             my $alt = GetFixed32s(\$val, 9);
1758             my $roleStr = {0=>'shooting',1=>'real',2=>'fictional',3=>'reserved'}->{$role};
1759             $str .= ' Role=' . ($roleStr || "unknown($role)");
1760             $str .= sprintf(' Lat=%.5f Lon=%.5f Alt=%.2f', $lat, $lon, $alt);
1761             $val = substr($val, 13);
1762             if ($val =~ s/^(\xfe\xff(.{2})*?)\0\0//s) {
1763             $str .= ' Body=' . $self->Decode($1, 'UCS2');
1764             } elsif ($val =~ s/^(.*?)\0//s) {
1765             $str .= ' Body=' . $self->Decode($1, 'UTF8');
1766             }
1767             if ($val =~ s/^(\xfe\xff(.{2})*?)\0\0//s) {
1768             $str .= ' Notes=' . $self->Decode($1, 'UCS2');
1769             } elsif ($val =~ s/^(.*?)\0//s) {
1770             $str .= ' Notes=' . $self->Decode($1, 'UTF8');
1771             }
1772             return $str;
1773             },
1774             RawConvInv => q{
1775             my ($role, $lat, $lon, $alt, $body, $note);
1776             $lat = $1 if $val =~ s/ Lat=([-+]?[.\d]+)//i;
1777             $lon = $1 if $val =~ s/ Lon=([-+]?[.\d]+)//i;
1778             $alt = $1 if $val =~ s/ Alt=([-+]?[.\d]+)//i;
1779             $note = $val =~ s/ Notes=(.*)//i ? $1 : '';
1780             $body = $val =~ s/ Body=(.*)//i ? $1 : '';
1781             $role = $val =~ s/ Role=(.*)//i ? $1 : '';
1782             $val = '' if $val eq '(none)';
1783             $role = {shooting=>0,real=>1,fictional=>2}->{lc $role} || 0;
1784             return $self->Encode($val, 'UTF8') . "\0" . Set8u($role) .
1785             SetFixed32s(defined $lon ? $lon : 999) .
1786             SetFixed32s(defined $lat ? $lat : 999) .
1787             SetFixed32s(defined $alt ? $alt : 0) .
1788             $self->Encode($body) . "\0" .
1789             $self->Encode($note) . "\0";
1790             },
1791             },
1792             yrrc => {
1793             Name => 'Year',
1794             Writable => 'undef',
1795             Groups => { 2 => 'Time' },
1796             Avoid => 1,
1797             Notes => 'used in 3gp videos',
1798             ValueConv => 'length($val) >= 6 ? unpack("x4n",$val) : ""',
1799             ValueConvInv => 'pack("Nn",0,$val)',
1800             },
1801             urat => { #17
1802             Name => 'UserRating',
1803             Writable => 'undef',
1804             Notes => 'used in 3gp videos',
1805             Avoid => 1,
1806             ValueConv => q{
1807             return '' unless length $val >= 8;
1808             unpack('x7C', $val);
1809             },
1810             ValueConvInv => 'pack("N2",0,$val)',
1811             },
1812             # tsel - TrackSelection (ref 17)
1813             # Apple tags (ref 16[dead] -- see ref 25 instead)
1814             angl => { Name => 'CameraAngle', Format => 'string' }, # (NC)
1815             clfn => { Name => 'ClipFileName', Format => 'string' }, # (NC)
1816             clid => { Name => 'ClipID', Format => 'string' }, # (NC)
1817             cmid => { Name => 'CameraID', Format => 'string' }, # (NC)
1818             cmnm => { # (NC)
1819             Name => 'Model',
1820             Description => 'Camera Model Name',
1821             Avoid => 1,
1822             Format => 'string', # (necessary to remove the trailing NULL)
1823             },
1824             date => {
1825             Name => 'DateTimeOriginal',
1826             Description => 'Date/Time Original',
1827             Groups => { 2 => 'Time' },
1828             Notes => q{
1829             Apple Photos has been reported to show a crazy date/time for some MP4 files
1830             containing this tag, but perhaps only if it is missing a time zone
1831             }, #forum10690/11125
1832             Shift => 'Time',
1833             ValueConv => q{
1834             require Image::ExifTool::XMP;
1835             $val = Image::ExifTool::XMP::ConvertXMPDate($val);
1836             $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
1837             return $val;
1838             },
1839             ValueConvInv => q{
1840             require Image::ExifTool::XMP;
1841             $val = Image::ExifTool::XMP::FormatXMPDate($val);
1842             $val =~ s/([-+]\d{2}):(\d{2})$/$1$2/; # remove time zone colon
1843             return $val;
1844             },
1845             PrintConv => '$self->ConvertDateTime($val)',
1846             PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
1847             },
1848             manu => { # (SX280)
1849             Name => 'Make',
1850             Avoid => 1,
1851             # (with Canon there are 6 unknown bytes before the model: "\0\0\0\0\x15\xc7")
1852             RawConv => '$val=~s/^\0{4}..//s; $val=~s/\0.*//; $val',
1853             },
1854             modl => { # (Samsung GT-S8530, Canon SX280)
1855             Name => 'Model',
1856             Description => 'Camera Model Name',
1857             Avoid => 1,
1858             # (with Canon there are 6 unknown bytes before the model: "\0\0\0\0\x15\xc7")
1859             RawConv => '$val=~s/^\0{4}..//s; $val=~s/\0.*//; $val',
1860             },
1861             reel => { Name => 'ReelName', Format => 'string' }, # (NC)
1862             scen => { Name => 'Scene', Format => 'string' }, # (NC)
1863             shot => { Name => 'ShotName', Format => 'string' }, # (NC)
1864             slno => { Name => 'SerialNumber', Format => 'string' }, # (NC)
1865             apmd => { Name => 'ApertureMode', Format => 'undef' }, #20
1866             kgtt => { #http://lists.ffmpeg.org/pipermail/ffmpeg-devel-irc/2012-June/000707.html
1867             # 'TrackType' will expand to 'Track#Type' when found inside a track
1868             Name => 'TrackType',
1869             # set flag to process this as international text
1870             # even though the tag ID doesn't start with 0xa9
1871             IText => 4, # IText with 4-byte header
1872             },
1873             chpl => { # (Nero chapter list)
1874             Name => 'ChapterList',
1875             ValueConv => \&ConvertChapterList,
1876             PrintConv => \&PrintChapter,
1877             },
1878             # ndrm - 7 bytes (0 0 0 1 0 0 0) Nero Digital Rights Management? (PH)
1879             # other non-Apple tags (ref 16)
1880             # hpix - HipixRichPicture (ref 16, HIPIX)
1881             # strk - sub-track information (ref 16, ISO)
1882             #
1883             # Manufacturer-specific metadata
1884             #
1885             TAGS => [ #PH
1886             # these tags were initially discovered in a Pentax movie,
1887             # but similar information is found in videos from other manufacturers
1888             {
1889             Name => 'FujiFilmTags',
1890             Condition => '$$valPt =~ /^FUJIFILM DIGITAL CAMERA\0/',
1891             SubDirectory => {
1892             TagTable => 'Image::ExifTool::FujiFilm::MOV',
1893             ByteOrder => 'LittleEndian',
1894             },
1895             },
1896             {
1897             Name => 'KodakTags',
1898             Condition => '$$valPt =~ /^EASTMAN KODAK COMPANY/',
1899             SubDirectory => {
1900             TagTable => 'Image::ExifTool::Kodak::MOV',
1901             ByteOrder => 'LittleEndian',
1902             },
1903             },
1904             {
1905             Name => 'KonicaMinoltaTags',
1906             Condition => '$$valPt =~ /^KONICA MINOLTA DIGITAL CAMERA/',
1907             SubDirectory => {
1908             TagTable => 'Image::ExifTool::Minolta::MOV1',
1909             ByteOrder => 'LittleEndian',
1910             },
1911             },
1912             {
1913             Name => 'MinoltaTags',
1914             Condition => '$$valPt =~ /^MINOLTA DIGITAL CAMERA/',
1915             SubDirectory => {
1916             TagTable => 'Image::ExifTool::Minolta::MOV2',
1917             ByteOrder => 'LittleEndian',
1918             },
1919             },
1920             {
1921             Name => 'NikonTags',
1922             Condition => '$$valPt =~ /^NIKON DIGITAL CAMERA\0/',
1923             SubDirectory => {
1924             TagTable => 'Image::ExifTool::Nikon::MOV',
1925             ByteOrder => 'LittleEndian',
1926             },
1927             },
1928             {
1929             Name => 'OlympusTags1',
1930             Condition => '$$valPt =~ /^OLYMPUS DIGITAL CAMERA\0.{9}\x01\0/s',
1931             SubDirectory => {
1932             TagTable => 'Image::ExifTool::Olympus::MOV1',
1933             ByteOrder => 'LittleEndian',
1934             },
1935             },
1936             {
1937             Name => 'OlympusTags2',
1938             Condition => '$$valPt =~ /^OLYMPUS DIGITAL CAMERA(?!\0.{21}\x0a\0{3})/s',
1939             SubDirectory => {
1940             TagTable => 'Image::ExifTool::Olympus::MOV2',
1941             ByteOrder => 'LittleEndian',
1942             },
1943             },
1944             {
1945             Name => 'OlympusTags3',
1946             Condition => '$$valPt =~ /^OLYMPUS DIGITAL CAMERA\0/',
1947             SubDirectory => {
1948             TagTable => 'Image::ExifTool::Olympus::MP4',
1949             ByteOrder => 'LittleEndian',
1950             },
1951             },
1952             {
1953             Name => 'OlympusTags4',
1954             Condition => '$$valPt =~ /^.{16}OLYM\0/s',
1955             SubDirectory => {
1956             TagTable => 'Image::ExifTool::Olympus::MOV3',
1957             Start => 12,
1958             },
1959             },
1960             {
1961             Name => 'PentaxTags',
1962             Condition => '$$valPt =~ /^PENTAX DIGITAL CAMERA\0/',
1963             SubDirectory => {
1964             TagTable => 'Image::ExifTool::Pentax::MOV',
1965             ByteOrder => 'LittleEndian',
1966             },
1967             },
1968             {
1969             Name => 'SamsungTags',
1970             Condition => '$$valPt =~ /^SAMSUNG DIGITAL CAMERA\0/',
1971             SubDirectory => {
1972             TagTable => 'Image::ExifTool::Samsung::MP4',
1973             ByteOrder => 'LittleEndian',
1974             },
1975             },
1976             {
1977             Name => 'SanyoMOV',
1978             Condition => q{
1979             $$valPt =~ /^SANYO DIGITAL CAMERA\0/ and
1980             $$self{FileType} eq "MOV"
1981             },
1982             SubDirectory => {
1983             TagTable => 'Image::ExifTool::Sanyo::MOV',
1984             ByteOrder => 'LittleEndian',
1985             },
1986             },
1987             {
1988             Name => 'SanyoMP4',
1989             Condition => q{
1990             $$valPt =~ /^SANYO DIGITAL CAMERA\0/ and
1991             $$self{FileType} eq "MP4"
1992             },
1993             SubDirectory => {
1994             TagTable => 'Image::ExifTool::Sanyo::MP4',
1995             ByteOrder => 'LittleEndian',
1996             },
1997             },
1998             {
1999             Name => 'UnknownTags',
2000             Unknown => 1,
2001             Binary => 1
2002             },
2003             ],
2004             # ---- Canon ----
2005             CNCV => { Name => 'CompressorVersion', Format => 'string' }, #PH (5D Mark II)
2006             CNMN => {
2007             Name => 'Model', #PH (EOS 550D)
2008             Description => 'Camera Model Name',
2009             Avoid => 1,
2010             Format => 'string', # (necessary to remove the trailing NULL)
2011             },
2012             CNFV => { Name => 'FirmwareVersion', Format => 'string' }, #PH (EOS 550D)
2013             CNTH => { #PH (PowerShot S95)
2014             Name => 'CanonCNTH',
2015             SubDirectory => { TagTable => 'Image::ExifTool::Canon::CNTH' },
2016             },
2017             CNOP => { #PH (7DmkII)
2018             Name => 'CanonCNOP',
2019             SubDirectory => { TagTable => 'Image::ExifTool::Canon::CNOP' },
2020             },
2021             # CNDB - 2112 bytes (550D)
2022             # CNDM - 4 bytes - 0xff,0xd8,0xff,0xd9 (S95)
2023             # CNDG - 10232 bytes, mostly zeros (N100)
2024             # ---- Casio ----
2025             QVMI => { #PH
2026             Name => 'CasioQVMI',
2027             # Casio stores standard EXIF-format information in MOV videos (eg. EX-S880)
2028             SubDirectory => {
2029             TagTable => 'Image::ExifTool::Exif::Main',
2030             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2031             DirName => 'IFD0',
2032             Multi => 0, # (no NextIFD pointer)
2033             Start => 10,
2034             ByteOrder => 'BigEndian',
2035             },
2036             },
2037             # ---- FujiFilm ----
2038             FFMV => { #PH (FinePix HS20EXR)
2039             Name => 'FujiFilmFFMV',
2040             SubDirectory => { TagTable => 'Image::ExifTool::FujiFilm::FFMV' },
2041             },
2042             MVTG => { #PH (FinePix HS20EXR)
2043             Name => 'FujiFilmMVTG',
2044             SubDirectory => {
2045             TagTable => 'Image::ExifTool::Exif::Main',
2046             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2047             DirName => 'IFD0',
2048             Start => 16,
2049             Base => '$start',
2050             ByteOrder => 'LittleEndian',
2051             },
2052             },
2053             # ---- Garmin ---- (ref PH)
2054             uuid => [{
2055             Name => 'GarminSoftware', # (NC)
2056             Condition => '$$valPt =~ /^VIRBactioncamera/',
2057             RawConv => 'substr($val, 16)',
2058             RawConvInv => '"VIRBactioncamera$val"',
2059             },{
2060             # 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)
2061             Name => 'UUID-Unknown',
2062             Writable => 0,
2063             %unknownInfo,
2064             }],
2065             pmcc => {
2066             Name => 'GarminSettings',
2067             ValueConv => 'substr($val, 4)',
2068             ValueConvInv => '"\0\0\0\x01$val"',
2069             },
2070             # hmtp - "\0\0\0\x01" followed by 408 bytes of zero
2071             # vrin - "\0\0\0\x01" followed by 8 bytes of zero
2072             # ---- GoPro ---- (ref PH)
2073             GoPr => 'GoProType', # (Hero3+)
2074             FIRM => { Name => 'FirmwareVersion', Avoid => 1 }, # (Hero4)
2075             LENS => 'LensSerialNumber', # (Hero4)
2076             CAME => { # (Hero4)
2077             Name => 'SerialNumberHash',
2078             Description => 'Camera Serial Number Hash',
2079             ValueConv => 'unpack("H*",$val)',
2080             ValueConvInv => 'pack("H*",$val)',
2081             },
2082             # SETT? 12 bytes (Hero4)
2083             # MUID? 32 bytes (Hero4, starts with serial number hash)
2084             # HMMT? 404 bytes (Hero4, all zero)
2085             # BCID? 26 bytes (Hero5, all zero), 36 bytes GoPro Max
2086             # GUMI? 16 bytes (Hero5)
2087             "FOV\0" => 'FieldOfView', #forum8938 (Hero2) seen: "Wide"
2088             GPMF => {
2089             Name => 'GoProGPMF',
2090             SubDirectory => { TagTable => 'Image::ExifTool::GoPro::GPMF' },
2091             },
2092             # free (all zero)
2093             "\xa9TSC" => 'StartTimeScale', # (Hero6)
2094             "\xa9TSZ" => 'StartTimeSampleSize', # (Hero6)
2095             "\xa9TIM" => 'StartTimecode', #PH (NC)
2096             # --- HTC ----
2097             htcb => {
2098             Name => 'HTCBinary',
2099             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HTCBinary' },
2100             },
2101             # ---- Kodak ----
2102             DcMD => {
2103             Name => 'KodakDcMD',
2104             SubDirectory => { TagTable => 'Image::ExifTool::Kodak::DcMD' },
2105             },
2106             SNum => { Name => 'SerialNumber', Avoid => 1, Groups => { 2 => 'Camera' } },
2107             ptch => { Name => 'Pitch', Format => 'rational64s', Avoid => 1 }, # Units??
2108             _yaw => { Name => 'Yaw', Format => 'rational64s', Avoid => 1 }, # Units??
2109             roll => { Name => 'Roll', Format => 'rational64s', Avoid => 1 }, # Units??
2110             _cx_ => { Name => 'CX', Format => 'rational64s', Unknown => 1 },
2111             _cy_ => { Name => 'CY', Format => 'rational64s', Unknown => 1 },
2112             rads => { Name => 'Rads', Format => 'rational64s', Unknown => 1 },
2113             lvlm => { Name => 'LevelMeter', Format => 'rational64s', Unknown => 1 }, # (guess)
2114             Lvlm => { Name => 'LevelMeter', Format => 'rational64s', Unknown => 1 }, # (guess)
2115             pose => { Name => 'pose', SubDirectory => { TagTable => 'Image::ExifTool::Kodak::pose' } },
2116             # AMBA => Ambarella AVC atom (unknown data written by Kodak Playsport video cam)
2117             # tmlp - 1 byte: 0 (PixPro SP360/4KVR360)
2118             # pivi - 72 bytes (PixPro SP360)
2119             # pive - 12 bytes (PixPro SP360)
2120             # loop - 4 bytes: 0 0 0 0 (PixPro 4KVR360)
2121             # m cm - 2 bytes: 0 0 (PixPro 4KVR360)
2122             # m ev - 2 bytes: 0 0 (PixPro SP360/4KVR360) (exposure comp?)
2123             # m vr - 2 bytes: 0 1 (PixPro 4KVR360) (virtual reality?)
2124             # m wb - 4 bytes: 0 0 0 0 (PixPro SP360/4KVR360) (white balance?)
2125             # mclr - 4 bytes: 0 0 0 0 (PixPro SP360/4KVR360)
2126             # mmtr - 4 bytes: 0,6 0 0 0 (PixPro SP360/4KVR360)
2127             # mflr - 4 bytes: 0 0 0 0 (PixPro SP360)
2128             # lvlm - 24 bytes (PixPro SP360)
2129             # Lvlm - 24 bytes (PixPro 4KVR360)
2130             # ufdm - 4 bytes: 0 0 0 1 (PixPro SP360)
2131             # mtdt - 1 byte: 0 (PixPro SP360/4KVR360)
2132             # gdta - 75240 bytes (PixPro SP360)
2133             # EIS1 - 4 bytes: 03 07 00 00 (PixPro 4KVR360)
2134             # EIS2 - 4 bytes: 04 97 00 00 (PixPro 4KVR360)
2135             # ---- LG ----
2136             adzc => { Name => 'Unknown_adzc', Unknown => 1, Hidden => 1, %langText }, # "false\0/","true\0/"
2137             adze => { Name => 'Unknown_adze', Unknown => 1, Hidden => 1, %langText }, # "false\0/"
2138             adzm => { Name => 'Unknown_adzm', Unknown => 1, Hidden => 1, %langText }, # "\x0e\x04/","\x10\x06"
2139             # ---- Microsoft ----
2140             Xtra => { #PH (microsoft)
2141             Name => 'MicrosoftXtra',
2142             WriteGroup => 'Microsoft',
2143             SubDirectory => {
2144             DirName => 'Microsoft',
2145             TagTable => 'Image::ExifTool::Microsoft::Xtra',
2146             },
2147             },
2148             # ---- Minolta ----
2149             MMA0 => { #PH (DiMage 7Hi)
2150             Name => 'MinoltaMMA0',
2151             SubDirectory => { TagTable => 'Image::ExifTool::Minolta::MMA' },
2152             },
2153             MMA1 => { #PH (Dimage A2)
2154             Name => 'MinoltaMMA1',
2155             SubDirectory => { TagTable => 'Image::ExifTool::Minolta::MMA' },
2156             },
2157             # ---- Nikon ----
2158             NCDT => { #PH
2159             Name => 'NikonNCDT',
2160             SubDirectory => { TagTable => 'Image::ExifTool::Nikon::NCDT' },
2161             },
2162             # ---- Olympus ----
2163             scrn => { #PH (TG-810)
2164             Name => 'OlympusPreview',
2165             Condition => '$$valPt =~ /^.{4}\xff\xd8\xff\xdb/s',
2166             SubDirectory => { TagTable => 'Image::ExifTool::Olympus::scrn' },
2167             },
2168             # ---- Panasonic/Leica ----
2169             PANA => { #PH
2170             Name => 'PanasonicPANA',
2171             SubDirectory => { TagTable => 'Image::ExifTool::Panasonic::PANA' },
2172             },
2173             LEIC => { #PH
2174             Name => 'LeicaLEIC',
2175             SubDirectory => { TagTable => 'Image::ExifTool::Panasonic::PANA' },
2176             },
2177             # ---- Pentax ----
2178             thmb => [ # (apparently defined by 3gpp, ref 16)
2179             { #PH (Pentax Q)
2180             Name => 'MakerNotePentax5a',
2181             Condition => '$$valPt =~ /^PENTAX \0II/',
2182             SubDirectory => {
2183             TagTable => 'Image::ExifTool::Pentax::Main',
2184             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2185             Start => 10,
2186             Base => '$start - 10',
2187             ByteOrder => 'LittleEndian',
2188             },
2189             },{ #PH (TG-810)
2190             Name => 'OlympusThumbnail',
2191             Condition => '$$valPt =~ /^.{4}\xff\xd8\xff\xdb/s',
2192             SubDirectory => { TagTable => 'Image::ExifTool::Olympus::thmb' },
2193             },{ #17 (format is in bytes 3-7)
2194             Name => 'ThumbnailImage',
2195             Condition => '$$valPt =~ /^.{8}\xff\xd8\xff[\xdb\xe0]/s',
2196             Groups => { 2 => 'Preview' },
2197             RawConv => 'substr($val, 8)',
2198             Binary => 1,
2199             },{ #17 (format is in bytes 3-7)
2200             Name => 'ThumbnailPNG',
2201             Condition => '$$valPt =~ /^.{8}\x89PNG\r\n\x1a\n/s',
2202             Groups => { 2 => 'Preview' },
2203             RawConv => 'substr($val, 8)',
2204             Binary => 1,
2205             },{
2206             Name => 'UnknownThumbnail',
2207             Groups => { 2 => 'Preview' },
2208             Binary => 1,
2209             },
2210             ],
2211             PENT => { #PH
2212             Name => 'PentaxPENT',
2213             SubDirectory => {
2214             TagTable => 'Image::ExifTool::Pentax::PENT',
2215             ByteOrder => 'LittleEndian',
2216             },
2217             },
2218             PXTH => { #PH (Pentax K-01)
2219             Name => 'PentaxPreview',
2220             SubDirectory => { TagTable => 'Image::ExifTool::Pentax::PXTH' },
2221             },
2222             PXMN => [{ #PH (Pentax K-01)
2223             Name => 'MakerNotePentax5b',
2224             Condition => '$$valPt =~ /^PENTAX \0MM/',
2225             SubDirectory => {
2226             TagTable => 'Image::ExifTool::Pentax::Main',
2227             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2228             Start => 10,
2229             Base => '$start - 10',
2230             ByteOrder => 'BigEndian',
2231             },
2232             },{ #PH (Pentax 645Z)
2233             Name => 'MakerNotePentax5c',
2234             Condition => '$$valPt =~ /^PENTAX \0II/',
2235             SubDirectory => {
2236             TagTable => 'Image::ExifTool::Pentax::Main',
2237             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2238             Start => 10,
2239             Base => '$start - 10',
2240             ByteOrder => 'LittleEndian',
2241             },
2242             },{
2243             Name => 'MakerNotePentaxUnknown',
2244             Binary => 1,
2245             }],
2246             # ---- Ricoh ----
2247             RTHU => { #PH (GR)
2248             Name => 'PreviewImage',
2249             Groups => { 2 => 'Preview' },
2250             RawConv => '$self->ValidateImage(\$val, $tag)',
2251             },
2252             RMKN => { #PH (GR)
2253             Name => 'RicohRMKN',
2254             SubDirectory => {
2255             TagTable => 'Image::ExifTool::Exif::Main',
2256             ProcessProc => \&Image::ExifTool::ProcessTIFF, # (because ProcessMOV is default)
2257             },
2258             },
2259             '@mak' => { Name => 'Make', Avoid => 1 },
2260             '@mod' => { Name => 'Model', Avoid => 1 },
2261             '@swr' => { Name => 'SoftwareVersion', Avoid => 1 },
2262             '@day' => {
2263             Name => 'ContentCreateDate',
2264             Notes => q{
2265             some stupid Ricoh programmer used the '@' symbol instead of the copyright
2266             symbol in these tag ID's for the Ricoh Theta Z1 and maybe other models
2267             },
2268             Groups => { 2 => 'Time' },
2269             Shift => 'Time',
2270             Avoid => 1,
2271             # handle values in the form "2010-02-12T13:27:14-0800"
2272             ValueConv => q{
2273             require Image::ExifTool::XMP;
2274             $val = Image::ExifTool::XMP::ConvertXMPDate($val);
2275             $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
2276             return $val;
2277             },
2278             ValueConvInv => q{
2279             require Image::ExifTool::XMP;
2280             my $tmp = Image::ExifTool::XMP::FormatXMPDate($val);
2281             ($val = $tmp) =~ s/([-+]\d{2}):(\d{2})$/$1$2/ if defined $tmp; # remove time zone colon
2282             return $val;
2283             },
2284             PrintConv => '$self->ConvertDateTime($val)',
2285             PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
2286             },
2287             '@xyz' => { #PH (iPhone 3GS)
2288             Name => 'GPSCoordinates',
2289             Groups => { 2 => 'Location' },
2290             Avoid => 1,
2291             ValueConv => \&ConvertISO6709,
2292             ValueConvInv => \&ConvInvISO6709,
2293             PrintConv => \&PrintGPSCoordinates,
2294             PrintConvInv => \&PrintInvGPSCoordinates,
2295             },
2296             # RDT1 - pairs of int32u_BE, starting at byte 8: "458275 471846"
2297             # RDT2 - pairs of int32u_BE, starting at byte 8: "472276 468526"
2298             # RDT3 - pairs of int32u_BE, starting at byte 8: "876603 482191"
2299             # RDT4 - pairs of int32u_BE, starting at byte 8: "1955 484612"
2300             # RDT6 - empty
2301             # RDT7 - empty
2302             # RDT8 - empty
2303             # RDT9 - only 16-byte header?
2304             # the boxes below all have a similar header (little-endian):
2305             # 0 int32u - number of records
2306             # 4 ? - "1e 00"
2307             # 6 int16u - record length in bytes
2308             # 8 ? - "23 01 00 00 00 00 00 00"
2309             # 16 - start of records (each record ends in an int64u timestamp "ts" in ns)
2310             # RDTA - float[4],ts: "-0.31289672 -0.2245330 11.303817 0 775.780"
2311             # RDTB - float[4],ts: "-0.04841613 -0.2166595 0.0724792 0 775.780"
2312             # RDTC - float[4],ts: "27.60925 -27.10037 -13.27285 0 775.829"
2313             # RDTD - int16s[3],ts: "353 -914 16354 0 775.829"
2314             # RDTG - ts: "775.825"
2315             # RDTI - float[4],ts: "0.00165951 0.005770059 0.06838259 0.1744695 775.862"
2316             # ---- Samsung ----
2317             vndr => 'Vendor', #PH (Samsung PL70)
2318             SDLN => 'PlayMode', #PH (NC, Samsung ST80 "SEQ_PLAY")
2319             INFO => {
2320             Name => 'SamsungINFO',
2321             SubDirectory => { TagTable => 'Image::ExifTool::Samsung::INFO' },
2322             },
2323             '@sec' => { #PH (Samsung WB30F)
2324             Name => 'SamsungSec',
2325             SubDirectory => { TagTable => 'Image::ExifTool::Samsung::sec' },
2326             },
2327             'smta' => { #PH (Samsung SM-C101)
2328             Name => 'SamsungSmta',
2329             SubDirectory => {
2330             TagTable => 'Image::ExifTool::Samsung::smta',
2331             Start => 4,
2332             },
2333             },
2334             cver => 'CodeVersion', #PH (guess, Samsung MV900F)
2335             # ducp - 4 bytes all zero (Samsung ST96,WB750), 52 bytes all zero (Samsung WB30F)
2336             # edli - 52 bytes all zero (Samsung WB30F)
2337             # @etc - 4 bytes all zero (Samsung WB30F)
2338             # saut - 4 bytes all zero (Samsung SM-N900T)
2339             # smrd - string "TRUEBLUE" (Samsung SM-C101)
2340             # ---- TomTom Bandit Action Cam ----
2341             TTMD => {
2342             Name => 'TomTomMetaData',
2343             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TomTom' },
2344             },
2345             # ---- Samsung Gear 360 ----
2346             vrot => {
2347             Name => 'AccelerometerData',
2348             Notes => q{
2349             accelerometer readings for each frame of the video, expressed as sets of
2350             yaw, pitch and roll angles in degrees
2351             },
2352             Format => 'rational64s',
2353             ValueConv => '$val =~ s/^-?\d+ //; \$val', # (ignore leading version/size words)
2354             },
2355             # m360 - 8 bytes "0 0 0 0 0 0 0 1"
2356             # opax - 164 bytes unknown (center and affine arrays? ref 26)
2357             # opai - 32 bytes (maybe contains a serial number starting at byte 16? - PH) (rgb gains, degamma, gamma? ref 26)
2358             # intv - 16 bytes all zero
2359             # ---- Xaiomi ----
2360             mcvr => {
2361             Name => 'PreviewImage',
2362             Groups => { 2 => 'Preview' },
2363             Binary => 1,
2364             },
2365             # ---- Unknown ----
2366             # CDET - 128 bytes (unknown origin)
2367             # mtyp - 4 bytes all zero (some drone video)
2368             # kgrf - 8 bytes all zero ? (in udta inside trak atom)
2369             # kgcg - 128 bytes 0's and 1's
2370             # kgsi - 4 bytes "00 00 00 80"
2371             # FIEL - 18 bytes "FIEL\0\x01\0\0\0..."
2372             #
2373             # other 3rd-party tags
2374             # (ref http://code.google.com/p/mp4parser/source/browse/trunk/isoparser/src/main/resources/isoparser-default.properties?r=814)
2375             #
2376             ccid => 'ContentID',
2377             icnu => 'IconURI',
2378             infu => 'InfoURL',
2379             cdis => 'ContentDistributorID',
2380             albr => { Name => 'AlbumArtist', Groups => { 2 => 'Author' } },
2381             cvru => 'CoverURI',
2382             lrcu => 'LyricsURI',
2383              
2384             tags => { # found in Audible .m4b audio books (ref PH)
2385             Name => 'Audible_tags',
2386             SubDirectory => { TagTable => 'Image::ExifTool::Audible::tags' },
2387             },
2388             );
2389              
2390             # Unknown information stored in HTC One (M8) videos - PH
2391             %Image::ExifTool::QuickTime::HTCBinary = (
2392             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2393             GROUPS => { 0 => 'MakerNotes', 1 => 'HTC', 2 => 'Video' },
2394             TAG_PREFIX => 'HTCBinary',
2395             FORMAT => 'int32u',
2396             FIRST_ENTRY => 0,
2397             # 0 - values: 1
2398             # 1 - values: 0
2399             # 2 - values: 0
2400             # 3 - values: FileSize minus 12 (why?)
2401             # 4 - values: 12
2402             );
2403              
2404             # TomTom Bandit Action Cam metadata (ref PH)
2405             %Image::ExifTool::QuickTime::TomTom = (
2406             PROCESS_PROC => \&ProcessMOV,
2407             GROUPS => { 2 => 'Video' },
2408             NOTES => 'Tags found in TomTom Bandit Action Cam MP4 videos.',
2409             TTAD => {
2410             Name => 'TomTomAD',
2411             SubDirectory => {
2412             TagTable => 'Image::ExifTool::QuickTime::Stream',
2413             ProcessProc => \&Image::ExifTool::QuickTime::ProcessTTAD,
2414             },
2415             },
2416             TTHL => { Name => 'TomTomHL', Binary => 1, Unknown => 1 }, # (mostly zeros)
2417             # (TTID values are different for each video)
2418             TTID => { Name => 'TomTomID', ValueConv => 'unpack("x4H*",$val)' },
2419             TTVI => { Name => 'TomTomVI', Format => 'int32u', Unknown => 1 }, # seen: "0 1 61 508 508"
2420             # TTVD seen: "normal 720p 60fps 60fps 16/9 wide 1x"
2421             TTVD => { Name => 'TomTomVD', ValueConv => 'my @a = ($val =~ /[\x20-\x7f]+/g); "@a"' },
2422             );
2423              
2424             # User-specific media data atoms (ref 11)
2425             %Image::ExifTool::QuickTime::UserMedia = (
2426             PROCESS_PROC => \&ProcessMOV,
2427             GROUPS => { 2 => 'Video' },
2428             MTDT => {
2429             Name => 'MetaData',
2430             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MetaData' },
2431             },
2432             );
2433              
2434             # User-specific media data atoms (ref 11)
2435             %Image::ExifTool::QuickTime::MetaData = (
2436             PROCESS_PROC => \&ProcessMetaData,
2437             GROUPS => { 2 => 'Video' },
2438             TAG_PREFIX => 'MetaData',
2439             0x01 => 'Title',
2440             0x03 => {
2441             Name => 'ProductionDate',
2442             Groups => { 2 => 'Time' },
2443             Shift => 'Time',
2444             Writable => 1,
2445             Permanent => 1,
2446             DelValue => '0000/00/00 00:00:00',
2447             # translate from format "YYYY/mm/dd HH:MM:SS"
2448             ValueConv => '$val=~tr{/}{:}; $val',
2449             ValueConvInv => '$val=~s[^(\d{4}):(\d{2}):][$1/$2/]; $val',
2450             PrintConv => '$self->ConvertDateTime($val)',
2451             PrintConvInv => '$self->InverseDateTime($val)',
2452             },
2453             0x04 => 'Software',
2454             0x05 => 'Product',
2455             0x0a => {
2456             Name => 'TrackProperty',
2457             RawConv => 'my @a=unpack("Nnn",$val); "@a"',
2458             PrintConv => [
2459             { 0 => 'No presentation', BITMASK => { 0 => 'Main track' } },
2460             { 0 => 'No attributes', BITMASK => { 15 => 'Read only' } },
2461             '"Priority $val"',
2462             ],
2463             },
2464             0x0b => {
2465             Name => 'TimeZone',
2466             Groups => { 2 => 'Time' },
2467             Writable => 1,
2468             Permanent => 1,
2469             DelValue => 0,
2470             RawConv => 'Get16s(\$val,0)',
2471             RawConvInv => 'Set16s($val)',
2472             PrintConv => 'TimeZoneString($val)',
2473             PrintConvInv => q{
2474             return undef unless $val =~ /^([-+])(\d{1,2}):?(\d{2})$/'
2475             my $tzmin = $2 * 60 + $3;
2476             $tzmin = -$tzmin if $1 eq '-';
2477             return $tzmin;
2478             }
2479             },
2480             0x0c => {
2481             Name => 'ModifyDate',
2482             Groups => { 2 => 'Time' },
2483             Shift => 'Time',
2484             Writable => 1,
2485             Permanent => 1,
2486             DelValue => '0000/00/00 00:00:00',
2487             # translate from format "YYYY/mm/dd HH:MM:SS"
2488             ValueConv => '$val=~tr{/}{:}; $val',
2489             ValueConvInv => '$val=~s[^(\d{4}):(\d{2}):][$1/$2/]; $val',
2490             PrintConv => '$self->ConvertDateTime($val)',
2491             PrintConvInv => '$self->InverseDateTime($val)',
2492             },
2493             );
2494              
2495             # compressed movie atoms (ref http://wiki.multimedia.cx/index.php?title=QuickTime_container#cmov)
2496             %Image::ExifTool::QuickTime::CMovie = (
2497             PROCESS_PROC => \&ProcessMOV,
2498             GROUPS => { 2 => 'Video' },
2499             dcom => 'Compression',
2500             # cmvd - compressed moov atom data
2501             );
2502              
2503             # Profile atoms (ref 11)
2504             %Image::ExifTool::QuickTime::Profile = (
2505             PROCESS_PROC => \&ProcessMOV,
2506             GROUPS => { 2 => 'Video' },
2507             FPRF => {
2508             Name => 'FileGlobalProfile',
2509             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::FileProf' },
2510             },
2511             APRF => {
2512             Name => 'AudioProfile',
2513             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AudioProf' },
2514             },
2515             VPRF => {
2516             Name => 'VideoProfile',
2517             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::VideoProf' },
2518             },
2519             OLYM => { #PH
2520             Name => 'OlympusOLYM',
2521             SubDirectory => {
2522             TagTable => 'Image::ExifTool::Olympus::OLYM',
2523             ByteOrder => 'BigEndian',
2524             },
2525             },
2526             );
2527              
2528             # FPRF atom information (ref 11)
2529             %Image::ExifTool::QuickTime::FileProf = (
2530             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2531             GROUPS => { 2 => 'Video' },
2532             FORMAT => 'int32u',
2533             0 => { Name => 'FileProfileVersion', Unknown => 1 }, # unknown = uninteresting
2534             1 => {
2535             Name => 'FileFunctionFlags',
2536             PrintConv => { BITMASK => {
2537             28 => 'Fragmented',
2538             29 => 'Additional tracks',
2539             30 => 'Edited', # (main AV track is edited)
2540             }},
2541             },
2542             # 2 - reserved
2543             );
2544              
2545             # APRF atom information (ref 11)
2546             %Image::ExifTool::QuickTime::AudioProf = (
2547             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2548             GROUPS => { 2 => 'Audio' },
2549             FORMAT => 'int32u',
2550             0 => { Name => 'AudioProfileVersion', Unknown => 1 },
2551             1 => 'AudioTrackID',
2552             2 => {
2553             Name => 'AudioCodec',
2554             Format => 'undef[4]',
2555             },
2556             3 => {
2557             Name => 'AudioCodecInfo',
2558             Unknown => 1,
2559             PrintConv => 'sprintf("0x%.4x", $val)',
2560             },
2561             4 => {
2562             Name => 'AudioAttributes',
2563             PrintConv => { BITMASK => {
2564             0 => 'Encrypted',
2565             1 => 'Variable bitrate',
2566             2 => 'Dual mono',
2567             }},
2568             },
2569             5 => {
2570             Name => 'AudioAvgBitrate',
2571             ValueConv => '$val * 1000',
2572             PrintConv => 'ConvertBitrate($val)',
2573             },
2574             6 => {
2575             Name => 'AudioMaxBitrate',
2576             ValueConv => '$val * 1000',
2577             PrintConv => 'ConvertBitrate($val)',
2578             },
2579             7 => 'AudioSampleRate',
2580             8 => 'AudioChannels',
2581             );
2582              
2583             # VPRF atom information (ref 11)
2584             %Image::ExifTool::QuickTime::VideoProf = (
2585             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2586             GROUPS => { 2 => 'Video' },
2587             FORMAT => 'int32u',
2588             0 => { Name => 'VideoProfileVersion', Unknown => 1 },
2589             1 => 'VideoTrackID',
2590             2 => {
2591             Name => 'VideoCodec',
2592             Format => 'undef[4]',
2593             },
2594             3 => {
2595             Name => 'VideoCodecInfo',
2596             Unknown => 1,
2597             PrintConv => 'sprintf("0x%.4x", $val)',
2598             },
2599             4 => {
2600             Name => 'VideoAttributes',
2601             PrintConv => { BITMASK => {
2602             0 => 'Encrypted',
2603             1 => 'Variable bitrate',
2604             2 => 'Variable frame rate',
2605             3 => 'Interlaced',
2606             }},
2607             },
2608             5 => {
2609             Name => 'VideoAvgBitrate',
2610             ValueConv => '$val * 1000',
2611             PrintConv => 'ConvertBitrate($val)',
2612             },
2613             6 => {
2614             Name => 'VideoMaxBitrate',
2615             ValueConv => '$val * 1000',
2616             PrintConv => 'ConvertBitrate($val)',
2617             },
2618             7 => {
2619             Name => 'VideoAvgFrameRate',
2620             Format => 'fixed32u',
2621             PrintConv => 'int($val * 1000 + 0.5) / 1000',
2622             },
2623             8 => {
2624             Name => 'VideoMaxFrameRate',
2625             Format => 'fixed32u',
2626             PrintConv => 'int($val * 1000 + 0.5) / 1000',
2627             },
2628             9 => {
2629             Name => 'VideoSize',
2630             Format => 'int16u[2]',
2631             PrintConv => '$val=~tr/ /x/; $val',
2632             },
2633             10 => {
2634             Name => 'PixelAspectRatio',
2635             Format => 'int16u[2]',
2636             PrintConv => '$val=~tr/ /:/; $val',
2637             },
2638             );
2639              
2640             # meta atoms
2641             %Image::ExifTool::QuickTime::Meta = (
2642             PROCESS_PROC => \&ProcessMOV,
2643             WRITE_PROC => \&WriteQuickTime,
2644             GROUPS => { 1 => 'Meta', 2 => 'Video' },
2645             ilst => {
2646             Name => 'ItemList',
2647             SubDirectory => {
2648             TagTable => 'Image::ExifTool::QuickTime::ItemList',
2649             HasData => 1, # process atoms as containers with 'data' elements
2650             },
2651             },
2652             # MP4 tags (ref 5)
2653             hdlr => {
2654             Name => 'Handler',
2655             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Handler' },
2656             },
2657             dinf => {
2658             Name => 'DataInfo', # (don't change this name -- used to recognize directory when writing)
2659             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::DataInfo' },
2660             },
2661             ipmc => {
2662             Name => 'IPMPControl',
2663             Flags => ['Binary','Unknown'],
2664             },
2665             iloc => {
2666             Name => 'ItemLocation',
2667             RawConv => \&ParseItemLocation,
2668             WriteHook => \&ParseItemLocation,
2669             Notes => 'parsed, but not extracted as a tag',
2670             },
2671             ipro => {
2672             Name => 'ItemProtection',
2673             Flags => ['Binary','Unknown'],
2674             },
2675             iinf => [{
2676             Name => 'ItemInformation',
2677             Condition => '$$valPt =~ /^\0/', # (check for version 0)
2678             SubDirectory => {
2679             TagTable => 'Image::ExifTool::QuickTime::ItemInfo',
2680             Start => 6, # (4-byte version/flags + 2-byte count)
2681             },
2682             },{
2683             Name => 'ItemInformation',
2684             SubDirectory => {
2685             TagTable => 'Image::ExifTool::QuickTime::ItemInfo',
2686             Start => 8, # (4-byte version/flags + 4-byte count)
2687             },
2688             }],
2689             'xml ' => {
2690             Name => 'XML',
2691             Flags => [ 'Binary', 'Protected' ],
2692             SubDirectory => {
2693             TagTable => 'Image::ExifTool::XMP::XML',
2694             IgnoreProp => { NonRealTimeMeta => 1 }, # ignore container for Sony 'nrtm'
2695             },
2696             },
2697             'keys' => {
2698             Name => 'Keys',
2699             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Keys' },
2700             },
2701             bxml => {
2702             Name => 'BinaryXML',
2703             Flags => ['Binary','Unknown'],
2704             },
2705             pitm => [{
2706             Name => 'PrimaryItemReference',
2707             Condition => '$$valPt =~ /^\0/', # (version 0?)
2708             RawConv => '$$self{PrimaryItem} = unpack("x4n",$val)',
2709             WriteHook => sub { my ($val,$et) = @_; $$et{PrimaryItem} = unpack("x4n",$val); },
2710             },{
2711             Name => 'PrimaryItemReference',
2712             RawConv => '$$self{PrimaryItem} = unpack("x4N",$val)',
2713             WriteHook => sub { my ($val,$et) = @_; $$et{PrimaryItem} = unpack("x4N",$val); },
2714             }],
2715             free => { #PH
2716             Name => 'Free',
2717             Flags => ['Binary','Unknown'],
2718             },
2719             iprp => {
2720             Name => 'ItemProperties',
2721             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ItemProp' },
2722             },
2723             iref => {
2724             Name => 'ItemReference',
2725             # the version is needed to parse some of the item references
2726             Condition => '$$self{ItemRefVersion} = ord($$valPt); 1',
2727             SubDirectory => {
2728             TagTable => 'Image::ExifTool::QuickTime::ItemRef',
2729             Start => 4,
2730             },
2731             },
2732             idat => {
2733             Name => 'MetaImageSize', #PH (NC)
2734             Format => 'int16u',
2735             # (don't know what the first two numbers are for)
2736             PrintConv => '$val =~ s/^(\d+) (\d+) (\d+) (\d+)/${3}x$4/; $val',
2737             },
2738             uuid => [
2739             { #PH (Canon R5/R6 HIF)
2740             Name => 'MetaVersion', # (NC)
2741             Condition => '$$valPt=~/^\x85\xc0\xb6\x87\x82\x0f\x11\xe0\x81\x11\xf4\xce\x46\x2b\x6a\x48/',
2742             RawConv => 'substr($val, 0x14)',
2743             },
2744             {
2745             Name => 'UUID-Unknown',
2746             %unknownInfo,
2747             },
2748             ],
2749             );
2750              
2751             # additional metadata container (ref ISO14496-12:2015)
2752             %Image::ExifTool::QuickTime::OtherMeta = (
2753             PROCESS_PROC => \&ProcessMOV,
2754             WRITE_PROC => \&WriteQuickTime,
2755             GROUPS => { 2 => 'Video' },
2756             mere => {
2757             Name => 'MetaRelation',
2758             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MetaRelation' },
2759             },
2760             meta => {
2761             Name => 'Meta',
2762             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
2763             },
2764             );
2765              
2766             # metabox relation (ref ISO14496-12:2015)
2767             %Image::ExifTool::QuickTime::MetaRelation = (
2768             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2769             GROUPS => { 2 => 'Video' },
2770             FORMAT => 'int32u',
2771             # 0 => 'MetaRelationVersion',
2772             # 1 => 'FirstMetaboxHandlerType',
2773             # 2 => 'FirstMetaboxHandlerType',
2774             # 3 => { Name => 'MetaboxRelation', Format => 'int8u' },
2775             );
2776              
2777             %Image::ExifTool::QuickTime::ItemProp = (
2778             PROCESS_PROC => \&ProcessMOV,
2779             WRITE_PROC => \&WriteQuickTime,
2780             GROUPS => { 2 => 'Image' },
2781             ipco => {
2782             Name => 'ItemPropertyContainer',
2783             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ItemPropCont' },
2784             },
2785             ipma => {
2786             Name => 'ItemPropertyAssociation',
2787             RawConv => \&ParseItemPropAssoc,
2788             WriteHook => \&ParseItemPropAssoc,
2789             Notes => 'parsed, but not extracted as a tag',
2790             },
2791             );
2792              
2793             %Image::ExifTool::QuickTime::ItemPropCont = (
2794             PROCESS_PROC => \&ProcessMOV,
2795             WRITE_PROC => \&WriteQuickTime,
2796             PERMANENT => 1, # (can't be deleted)
2797             GROUPS => { 2 => 'Image' },
2798             VARS => { START_INDEX => 1 }, # show verbose indices starting at 1
2799             colr => [{
2800             Name => 'ICC_Profile',
2801             Condition => '$$valPt =~ /^(prof|rICC)/',
2802             Permanent => 0, # (in QuickTime, this writes a zero-length box instead of deleting)
2803             SubDirectory => {
2804             TagTable => 'Image::ExifTool::ICC_Profile::Main',
2805             Start => 4,
2806             },
2807             },{
2808             Name => 'ColorRepresentation',
2809             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ColorRep' },
2810             }],
2811             irot => {
2812             Name => 'Rotation',
2813             Format => 'int8u',
2814             Writable => 'int8u',
2815             Protected => 1,
2816             ValueConv => '$val * 90',
2817             ValueConvInv => 'int($val / 90 + 0.5)',
2818             },
2819             ispe => {
2820             Name => 'ImageSpatialExtent',
2821             Condition => '$$valPt =~ /^\0{4}/', # (version/flags == 0/0)
2822             RawConv => q{
2823             my @dim = unpack("x4N*", $val);
2824             return undef if @dim < 2;
2825             unless ($$self{DOC_NUM}) {
2826             $self->FoundTag(ImageWidth => $dim[0]);
2827             $self->FoundTag(ImageHeight => $dim[1]);
2828             }
2829             return join ' ', @dim;
2830             },
2831             PrintConv => '$val =~ tr/ /x/; $val',
2832             },
2833             pixi => {
2834             Name => 'ImagePixelDepth',
2835             Condition => '$$valPt =~ /^\0{4}./s', # (version/flags == 0/0 and count)
2836             RawConv => 'join " ", unpack("x5C*", $val)',
2837             },
2838             auxC => {
2839             Name => 'AuxiliaryImageType',
2840             Format => 'undef',
2841             RawConv => '$val = substr($val, 4); $val =~ s/\0.*//s; $val',
2842             },
2843             pasp => {
2844             Name => 'PixelAspectRatio',
2845             Format => 'int32u',
2846             Writable => 'int32u',
2847             Protected => 1,
2848             },
2849             rloc => {
2850             Name => 'RelativeLocation',
2851             Format => 'int32u',
2852             RawConv => '$val =~ s/^\S+\s+//; $val', # remove version/flags
2853             },
2854             clap => {
2855             Name => 'CleanAperture',
2856             Format => 'rational64s',
2857             Notes => '4 numbers: width, height, left and top',
2858             },
2859             hvcC => {
2860             Name => 'HEVCConfiguration',
2861             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HEVCConfig' },
2862             },
2863             av1C => {
2864             Name => 'AV1Configuration',
2865             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AV1Config' },
2866             },
2867             );
2868              
2869             # ref https://aomediacodec.github.io/av1-spec/av1-spec.pdf
2870             %Image::ExifTool::QuickTime::ColorRep = (
2871             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2872             GROUPS => { 2 => 'Video' },
2873             FIRST_ENTRY => 0,
2874             0 => { Name => 'ColorProfiles', Format => 'undef[4]' },
2875             4 => {
2876             Name => 'ColorPrimaries',
2877             Format => 'int16u',
2878             PrintConv => {
2879             1 => 'BT.709',
2880             2 => 'Unspecified',
2881             4 => 'BT.470 System M (historical)',
2882             5 => 'BT.470 System B, G (historical)',
2883             6 => 'BT.601',
2884             7 => 'SMPTE 240',
2885             8 => 'Generic film (color filters using illuminant C)',
2886             9 => 'BT.2020, BT.2100',
2887             10 => 'SMPTE 428 (CIE 1921 XYZ)',
2888             11 => 'SMPTE RP 431-2',
2889             12 => 'SMPTE EG 432-1',
2890             22 => 'EBU Tech. 3213-E',
2891             },
2892             },
2893             6 => {
2894             Name => 'TransferCharacteristics',
2895             Format => 'int16u',
2896             PrintConv => {
2897             0 => 'For future use (0)',
2898             1 => 'BT.709',
2899             2 => 'Unspecified',
2900             3 => 'For future use (3)',
2901             4 => 'BT.470 System M (historical)',
2902             5 => 'BT.470 System B, G (historical)',
2903             6 => 'BT.601',
2904             7 => 'SMPTE 240 M',
2905             8 => 'Linear',
2906             9 => 'Logarithmic (100 : 1 range)',
2907             10 => 'Logarithmic (100 * Sqrt(10) : 1 range)',
2908             11 => 'IEC 61966-2-4',
2909             12 => 'BT.1361',
2910             13 => 'sRGB or sYCC',
2911             14 => 'BT.2020 10-bit systems',
2912             15 => 'BT.2020 12-bit systems',
2913             16 => 'SMPTE ST 2084, ITU BT.2100 PQ',
2914             17 => 'SMPTE ST 428',
2915             18 => 'BT.2100 HLG, ARIB STD-B67',
2916             },
2917             },
2918             8 => {
2919             Name => 'MatrixCoefficients',
2920             Format => 'int16u',
2921             PrintConv => {
2922             0 => 'Identity matrix',
2923             1 => 'BT.709',
2924             2 => 'Unspecified',
2925             3 => 'For future use (3)',
2926             4 => 'US FCC 73.628',
2927             5 => 'BT.470 System B, G (historical)',
2928             6 => 'BT.601',
2929             7 => 'SMPTE 240 M',
2930             8 => 'YCgCo',
2931             9 => 'BT.2020 non-constant luminance, BT.2100 YCbCr',
2932             10 => 'BT.2020 constant luminance',
2933             11 => 'SMPTE ST 2085 YDzDx',
2934             12 => 'Chromaticity-derived non-constant luminance',
2935             13 => 'Chromaticity-derived constant luminance',
2936             14 => 'BT.2100 ICtCp',
2937             },
2938             },
2939             );
2940              
2941             # HEVC configuration (ref https://github.com/MPEGGroup/isobmff/blob/master/IsoLib/libisomediafile/src/HEVCConfigAtom.c)
2942             %Image::ExifTool::QuickTime::HEVCConfig = (
2943             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2944             GROUPS => { 2 => 'Video' },
2945             FIRST_ENTRY => 0,
2946             0 => 'HEVCConfigurationVersion',
2947             1 => {
2948             Name => 'GeneralProfileSpace',
2949             Mask => 0xc0,
2950             PrintConv => { 0 => 'Conforming' },
2951             },
2952             1.1 => {
2953             Name => 'GeneralTierFlag',
2954             Mask => 0x20,
2955             PrintConv => {
2956             0 => 'Main Tier',
2957             1 => 'High Tier',
2958             },
2959             },
2960             1.2 => {
2961             Name => 'GeneralProfileIDC',
2962             Mask => 0x1f,
2963             PrintConv => {
2964             0 => 'No Profile',
2965             1 => 'Main',
2966             2 => 'Main 10',
2967             3 => 'Main Still Picture',
2968             4 => 'Format Range Extensions',
2969             5 => 'High Throughput',
2970             6 => 'Multiview Main',
2971             7 => 'Scalable Main',
2972             8 => '3D Main',
2973             9 => 'Screen Content Coding Extensions',
2974             10 => 'Scalable Format Range Extensions',
2975             11 => 'High Throughput Screen Content Coding Extensions',
2976             },
2977             },
2978             2 => {
2979             Name => 'GenProfileCompatibilityFlags',
2980             Format => 'int32u',
2981             PrintConv => { BITMASK => {
2982             31 => 'No Profile', # (bit 0 in stream)
2983             30 => 'Main', # (bit 1 in stream)
2984             29 => 'Main 10', # (bit 2 in stream)
2985             28 => 'Main Still Picture', # (bit 3 in stream)
2986             27 => 'Format Range Extensions',# (...)
2987             26 => 'High Throughput',
2988             25 => 'Multiview Main',
2989             24 => 'Scalable Main',
2990             23 => '3D Main',
2991             22 => 'Screen Content Coding Extensions',
2992             21 => 'Scalable Format Range Extensions',
2993             20 => 'High Throughput Screen Content Coding Extensions',
2994             }},
2995             },
2996             6 => {
2997             Name => 'ConstraintIndicatorFlags',
2998             Format => 'int8u[6]',
2999             },
3000             12 => {
3001             Name => 'GeneralLevelIDC',
3002             PrintConv => 'sprintf("%d (level %.1f)", $val, $val/30)',
3003             },
3004             13 => {
3005             Name => 'MinSpatialSegmentationIDC',
3006             Format => 'int16u',
3007             Mask => 0x0fff,
3008             },
3009             15 => {
3010             Name => 'ParallelismType',
3011             Mask => 0x03,
3012             },
3013             16 => {
3014             Name => 'ChromaFormat',
3015             Mask => 0x03,
3016             PrintConv => {
3017             0 => 'Monochrome',
3018             1 => '4:2:0',
3019             2 => '4:2:2',
3020             3 => '4:4:4',
3021             },
3022             },
3023             17 => {
3024             Name => 'BitDepthLuma',
3025             Mask => 0x07,
3026             ValueConv => '$val + 8',
3027             },
3028             18 => {
3029             Name => 'BitDepthChroma',
3030             Mask => 0x07,
3031             ValueConv => '$val + 8',
3032             },
3033             19 => {
3034             Name => 'AverageFrameRate',
3035             Format => 'int16u',
3036             ValueConv => '$val / 256',
3037             },
3038             21 => {
3039             Name => 'ConstantFrameRate',
3040             Mask => 0xc0,
3041             PrintConv => {
3042             0 => 'Unknown',
3043             1 => 'Constant Frame Rate',
3044             2 => 'Each Temporal Layer is Constant Frame Rate',
3045             },
3046             },
3047             21.1 => {
3048             Name => 'NumTemporalLayers',
3049             Mask => 0x38,
3050             },
3051             21.2 => {
3052             Name => 'TemporalIDNested',
3053             Mask => 0x04,
3054             PrintConv => { 0 => 'No', 1 => 'Yes' },
3055             },
3056             #21.3 => {
3057             # Name => 'NALUnitLengthSize',
3058             # Mask => 0x03,
3059             # ValueConv => '$val + 1',
3060             # PrintConv => { 1 => '8-bit', 2 => '16-bit', 4 => '32-bit' },
3061             #},
3062             #22 => 'NumberOfNALUnitArrays',
3063             # (don't decode the NAL unit arrays)
3064             );
3065              
3066             # HEVC configuration (ref https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox)
3067             %Image::ExifTool::QuickTime::AV1Config = (
3068             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
3069             GROUPS => { 2 => 'Video' },
3070             FIRST_ENTRY => 0,
3071             0 => {
3072             Name => 'AV1ConfigurationVersion',
3073             Mask => 0x7f,
3074             },
3075             1.0 => {
3076             Name => 'SeqProfile',
3077             Mask => 0xe0,
3078             Unknown => 1,
3079             },
3080             1.1 => {
3081             Name => 'SeqLevelIdx0',
3082             Mask => 0x1f,
3083             Unknown => 1,
3084             },
3085             2.0 => {
3086             Name => 'SeqTier0',
3087             Mask => 0x80,
3088             Unknown => 1,
3089             },
3090             2.1 => {
3091             Name => 'HighBitDepth',
3092             Mask => 0x40,
3093             Unknown => 1,
3094             },
3095             2.2 => {
3096             Name => 'TwelveBit',
3097             Mask => 0x20,
3098             Unknown => 1,
3099             },
3100             2.3 => {
3101             Name => 'ChromaFormat', # (Monochrome+SubSamplingX+SubSamplingY)
3102             Notes => 'bits: 0x04 = Monochrome, 0x02 = SubSamplingX, 0x01 = SubSamplingY',
3103             Mask => 0x1c,
3104             PrintConv => {
3105             0x00 => 'YUV 4:4:4',
3106             0x02 => 'YUV 4:2:2',
3107             0x03 => 'YUV 4:2:0',
3108             0x07 => 'Monochrome 4:0:0',
3109             },
3110             },
3111             2.4 => {
3112             Name => 'ChromaSamplePosition',
3113             Mask => 0x03,
3114             PrintConv => {
3115             0 => 'Unknown',
3116             1 => 'Vertical',
3117             2 => 'Colocated',
3118             3 => '(reserved)',
3119             },
3120             },
3121             3 => {
3122             Name => 'InitialDelaySamples',
3123             RawConv => '$val & 0x10 ? undef : ($val & 0x0f) + 1',
3124             Unknown => 1,
3125             },
3126             );
3127              
3128             %Image::ExifTool::QuickTime::ItemRef = (
3129             PROCESS_PROC => \&ProcessMOV,
3130             WRITE_PROC => \&WriteQuickTime,
3131             GROUPS => { 2 => 'Image' },
3132             # (Note: ExifTool's ItemRefVersion may be used to test the iref version number)
3133             NOTES => q{
3134             The Item reference entries listed in the table below contain information about
3135             the associations between items in the file. This information is used by
3136             ExifTool, but these entries are not extracted as tags.
3137             },
3138             dimg => { Name => 'DerivedImageRef', RawConv => 'undef' },
3139             thmb => { Name => 'ThumbnailRef', RawConv => 'undef' },
3140             auxl => { Name => 'AuxiliaryImageRef', RawConv => 'undef' },
3141             cdsc => {
3142             Name => 'ContentDescribes',
3143             RawConv => \&ParseContentDescribes,
3144             WriteHook => \&ParseContentDescribes,
3145             },
3146             );
3147              
3148             %Image::ExifTool::QuickTime::ItemInfo = (
3149             PROCESS_PROC => \&ProcessMOV,
3150             WRITE_PROC => \&WriteQuickTime,
3151             GROUPS => { 2 => 'Image' },
3152             # avc1 - AVC image
3153             # hvc1 - HEVC image
3154             # lhv1 - L-HEVC image
3155             # infe - ItemInformationEntry
3156             # infe types: avc1,hvc1,lhv1,Exif,xml1,iovl(overlay image),grid,mime,hvt1(tile image)
3157             infe => {
3158             Name => 'ItemInfoEntry',
3159             RawConv => \&ParseItemInfoEntry,
3160             WriteHook => \&ParseItemInfoEntry,
3161             Notes => 'parsed, but not extracted as a tag',
3162             },
3163             );
3164              
3165             # track reference atoms
3166             %Image::ExifTool::QuickTime::TrackRef = (
3167             PROCESS_PROC => \&ProcessMOV,
3168             GROUPS => { 1 => 'Track#', 2 => 'Video' },
3169             chap => { Name => 'ChapterListTrackID', Format => 'int32u' },
3170             tmcd => { Name => 'TimeCode', Format => 'int32u' },
3171             mpod => { #PH (FLIR MP4)
3172             Name => 'ElementaryStreamTrack',
3173             Format => 'int32u',
3174             ValueConv => '$val =~ s/^1 //; $val', # (why 2 numbers? -- ignore the first if "1")
3175             },
3176             # also: sync, scpt, ssrc, iTunesInfo
3177             cdsc => {
3178             Name => 'ContentDescribes',
3179             Format => 'int32u',
3180             PrintConv => '"Track $val"',
3181             },
3182             # cdep (Structural Dependency QT tag?)
3183             );
3184              
3185             # track aperture mode dimensions atoms
3186             # (ref https://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap2/qtff2.html)
3187             %Image::ExifTool::QuickTime::TrackAperture = (
3188             PROCESS_PROC => \&ProcessMOV,
3189             GROUPS => { 1 => 'Track#', 2 => 'Video' },
3190             clef => {
3191             Name => 'CleanApertureDimensions',
3192             Format => 'fixed32u',
3193             Count => 3,
3194             ValueConv => '$val =~ s/^.*? //; $val', # remove flags word
3195             PrintConv => '$val =~ tr/ /x/; $val',
3196             },
3197             prof => {
3198             Name => 'ProductionApertureDimensions',
3199             Format => 'fixed32u',
3200             Count => 3,
3201             ValueConv => '$val =~ s/^.*? //; $val',
3202             PrintConv => '$val =~ tr/ /x/; $val',
3203             },
3204             enof => {
3205             Name => 'EncodedPixelsDimensions',
3206             Format => 'fixed32u',
3207             Count => 3,
3208             ValueConv => '$val =~ s/^.*? //; $val',
3209             PrintConv => '$val =~ tr/ /x/; $val',
3210             },
3211             );
3212              
3213             # item list atoms
3214             # -> these atoms are unique, and contain one or more 'data' atoms
3215             %Image::ExifTool::QuickTime::ItemList = (
3216             PROCESS_PROC => \&ProcessMOV,
3217             WRITE_PROC => \&WriteQuickTime,
3218             CHECK_PROC => \&CheckQTValue,
3219             WRITABLE => 1,
3220             PREFERRED => 2, # (preferred over UserData and Keys tags when writing)
3221             FORMAT => 'string',
3222             GROUPS => { 1 => 'ItemList', 2 => 'Audio' },
3223             WRITE_GROUP => 'ItemList',
3224             LANG_INFO => \&GetLangInfo,
3225             NOTES => q{
3226             This is the preferred location for creating new QuickTime tags. Tags in
3227             this table support alternate languages which are accessed by adding a
3228             3-character ISO 639-2 language code and an optional ISO 3166-1 alpha 2
3229             country code to the tag name (eg. "ItemList:Title-fra" or
3230             "ItemList::Title-fra-FR"). When creating a new Meta box to contain the
3231             ItemList directory, by default ExifTool adds an 'mdir' (Metadata) Handler
3232             box because Apple software may ignore ItemList tags otherwise, but the API
3233             L option may be set to 0 to avoid this.
3234             },
3235             # in this table, binary 1 and 2-byte "data"-type tags are interpreted as
3236             # int8u and int16u. Multi-byte binary "data" tags are extracted as binary data.
3237             # (Note that the Preferred property is set to 0 for some tags to prevent them
3238             # from being created when a same-named tag already exists in the table)
3239             "\xa9ART" => 'Artist',
3240             "\xa9alb" => 'Album',
3241             "\xa9aut" => { Name => 'Author', Avoid => 1, Groups => { 2 => 'Author' } }, #forum10091 ('auth' is preferred)
3242             "\xa9cmt" => 'Comment',
3243             "\xa9com" => { Name => 'Composer', Avoid => 1, }, # ("\xa9wrt" is preferred in ItemList)
3244             "\xa9day" => {
3245             Name => 'ContentCreateDate',
3246             Groups => { 2 => 'Time' },
3247             Shift => 'Time',
3248             # handle values in the form "2010-02-12T13:27:14-0800"
3249             ValueConv => q{
3250             require Image::ExifTool::XMP;
3251             $val = Image::ExifTool::XMP::ConvertXMPDate($val);
3252             $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
3253             return $val;
3254             },
3255             ValueConvInv => q{
3256             require Image::ExifTool::XMP;
3257             $val = Image::ExifTool::XMP::FormatXMPDate($val);
3258             $val =~ s/([-+]\d{2}):(\d{2})$/$1$2/; # remove time zone colon
3259             return $val;
3260             },
3261             PrintConv => '$self->ConvertDateTime($val)',
3262             PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
3263             },
3264             "\xa9des" => 'Description', #4
3265             "\xa9enc" => 'EncodedBy', #10
3266             "\xa9gen" => 'Genre',
3267             "\xa9grp" => 'Grouping',
3268             "\xa9lyr" => 'Lyrics',
3269             "\xa9nam" => 'Title',
3270             "\xa9too" => 'Encoder',
3271             "\xa9trk" => 'Track',
3272             "\xa9wrt" => 'Composer',
3273             #
3274             # the following tags written by AtomicParsley 0.9.6
3275             # (ref https://exiftool.org/forum/index.php?topic=11455.0)
3276             #
3277             "\xa9st3" => 'Subtitle',
3278             "\xa9con" => 'Conductor',
3279             "\xa9sol" => 'Soloist',
3280             "\xa9arg" => 'Arranger',
3281             "\xa9ope" => 'OriginalArtist',
3282             "\xa9dir" => 'Director',
3283             "\xa9ard" => 'ArtDirector',
3284             "\xa9sne" => 'SoundEngineer',
3285             "\xa9prd" => 'Producer',
3286             "\xa9xpd" => 'ExecutiveProducer',
3287             sdes => 'StoreDescription',
3288             #
3289             '----' => {
3290             Name => 'iTunesInfo',
3291             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::iTunesInfo' },
3292             },
3293             aART => { Name => 'AlbumArtist', Groups => { 2 => 'Author' } },
3294             covr => { Name => 'CoverArt', Groups => { 2 => 'Preview' } },
3295             cpil => { #10
3296             Name => 'Compilation',
3297             Format => 'int8u', #27 (ref 23 contradicts what AtomicParsley actually writes, which is int8s)
3298             Writable => 'int8s',
3299             PrintConv => { 0 => 'No', 1 => 'Yes' },
3300             },
3301             disk => {
3302             Name => 'DiskNumber',
3303             Format => 'undef', # (necessary to prevent decoding as string!)
3304             ValueConv => q{
3305             return \$val unless length($val) >= 6;
3306             my @a = unpack 'x2nn', $val;
3307             return $a[1] ? join(' of ', @a) : $a[0];
3308             },
3309             ValueConvInv => q{
3310             my @a = $val =~ /\d+/g;
3311             return undef if @a == 0 or @a > 2;
3312             push @a, 0 if @a == 1;
3313             return pack('n3', 0, @a);
3314             },
3315             },
3316             pgap => { #10
3317             Name => 'PlayGap',
3318             Format => 'int8u', #23
3319             Writable => 'int8s', #27
3320             PrintConv => {
3321             0 => 'Insert Gap',
3322             1 => 'No Gap',
3323             },
3324             },
3325             tmpo => {
3326             Name => 'BeatsPerMinute',
3327             # marked as boolean but really int16u in my sample
3328             # (but written as int16s by iTunes and AtomicParsley, ref forum11506)
3329             Format => 'int16u',
3330             Writable => 'int16s',
3331             },
3332             trkn => {
3333             Name => 'TrackNumber',
3334             Format => 'undef', # (necessary to prevent decoding as string!)
3335             ValueConv => q{
3336             return \$val unless length($val) >= 6;
3337             my @a = unpack 'x2nn', $val;
3338             return $a[1] ? join(' of ', @a) : $a[0];
3339             },
3340             # (see forum11501 for discussion about the format used)
3341             ValueConvInv => q{
3342             my @a = $val =~ /\d+/g;
3343             return undef if @a == 0 or @a > 2;
3344             push @a, 0 if @a == 1;
3345             return pack('n4', 0, @a, 0);
3346             },
3347             },
3348             #
3349             # Note: it is possible that the tags below are not being decoded properly
3350             # because I don't have samples to verify many of these - PH
3351             #
3352             akID => { #10
3353             Name => 'AppleStoreAccountType',
3354             Format => 'int8u', #24
3355             Writable => 'int8s', #27
3356             PrintConv => {
3357             0 => 'iTunes',
3358             1 => 'AOL',
3359             },
3360             },
3361             albm => { Name => 'Album', Avoid => 1 }, #(ffmpeg source)
3362             apID => 'AppleStoreAccount',
3363             atID => { #10 (or TV series)
3364             Name => 'AlbumTitleID',
3365             Format => 'int32u',
3366             Writable => 'int32s', #27
3367             },
3368             auth => { Name => 'Author', Groups => { 2 => 'Author' } },
3369             catg => 'Category', #7
3370             cnID => { #10
3371             Name => 'AppleStoreCatalogID',
3372             Format => 'int32u',
3373             Writable => 'int32s', #27
3374             },
3375             cprt => { Name => 'Copyright', Groups => { 2 => 'Author' } },
3376             dscp => { Name => 'Description', Avoid => 1 },
3377             desc => { Name => 'Description', Avoid => 1 }, #7
3378             gnre => { #10
3379             Name => 'Genre',
3380             Avoid => 1,
3381             # (Note: see https://exiftool.org/forum/index.php?topic=11537.0)
3382             Format => 'undef',
3383             ValueConv => 'unpack("n",$val)',
3384             ValueConvInv => '$val =~ /^\d+$/ ? pack("n",$val) : undef',
3385             PrintConv => q{
3386             return $val unless $val =~ /^\d+$/;
3387             require Image::ExifTool::ID3;
3388             Image::ExifTool::ID3::PrintGenre($val - 1); # note the "- 1"
3389             },
3390             PrintConvInv => q{
3391             return $val if $val =~ /^[0-9]+$/;
3392             require Image::ExifTool::ID3;
3393             my $id = Image::ExifTool::ID3::GetGenreID($val);
3394             return unless defined $id and $id =~ /^\d+$/;
3395             return $id + 1;
3396             },
3397             },
3398             egid => 'EpisodeGlobalUniqueID', #7
3399             geID => { #10
3400             Name => 'GenreID',
3401             Format => 'int32u',
3402             Writable => 'int32s', #27
3403             SeparateTable => 1,
3404             # the following lookup is based on http://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/genres
3405             # (see scripts/parse_genre to parse genre JSON file from above)
3406             PrintConv => { #21/PH
3407             2 => 'Music|Blues',
3408             3 => 'Music|Comedy',
3409             4 => "Music|Children's Music",
3410             5 => 'Music|Classical',
3411             6 => 'Music|Country',
3412             7 => 'Music|Electronic',
3413             8 => 'Music|Holiday',
3414             9 => 'Music|Classical|Opera',
3415             10 => 'Music|Singer/Songwriter',
3416             11 => 'Music|Jazz',
3417             12 => 'Music|Latino',
3418             13 => 'Music|New Age',
3419             14 => 'Music|Pop',
3420             15 => 'Music|R&B/Soul',
3421             16 => 'Music|Soundtrack',
3422             17 => 'Music|Dance',
3423             18 => 'Music|Hip-Hop/Rap',
3424             19 => 'Music|World',
3425             20 => 'Music|Alternative',
3426             21 => 'Music|Rock',
3427             22 => 'Music|Christian & Gospel',
3428             23 => 'Music|Vocal',
3429             24 => 'Music|Reggae',
3430             25 => 'Music|Easy Listening',
3431             26 => 'Podcasts',
3432             27 => 'Music|J-Pop',
3433             28 => 'Music|Enka',
3434             29 => 'Music|Anime',
3435             30 => 'Music|Kayokyoku',
3436             31 => 'Music Videos',
3437             32 => 'TV Shows',
3438             33 => 'Movies',
3439             34 => 'Music',
3440             35 => 'iPod Games',
3441             36 => 'App Store',
3442             37 => 'Tones',
3443             38 => 'Books',
3444             39 => 'Mac App Store',
3445             40 => 'Textbooks',
3446             50 => 'Music|Fitness & Workout',
3447             51 => 'Music|Pop|K-Pop',
3448             52 => 'Music|Karaoke',
3449             53 => 'Music|Instrumental',
3450             74 => 'Audiobooks|News',
3451             75 => 'Audiobooks|Programs & Performances',
3452             500 => 'Fitness Music',
3453             501 => 'Fitness Music|Pop',
3454             502 => 'Fitness Music|Dance',
3455             503 => 'Fitness Music|Hip-Hop',
3456             504 => 'Fitness Music|Rock',
3457             505 => 'Fitness Music|Alt/Indie',
3458             506 => 'Fitness Music|Latino',
3459             507 => 'Fitness Music|Country',
3460             508 => 'Fitness Music|World',
3461             509 => 'Fitness Music|New Age',
3462             510 => 'Fitness Music|Classical',
3463             1001 => 'Music|Alternative|College Rock',
3464             1002 => 'Music|Alternative|Goth Rock',
3465             1003 => 'Music|Alternative|Grunge',
3466             1004 => 'Music|Alternative|Indie Rock',
3467             1005 => 'Music|Alternative|New Wave',
3468             1006 => 'Music|Alternative|Punk',
3469             1007 => 'Music|Blues|Chicago Blues',
3470             1009 => 'Music|Blues|Classic Blues',
3471             1010 => 'Music|Blues|Contemporary Blues',
3472             1011 => 'Music|Blues|Country Blues',
3473             1012 => 'Music|Blues|Delta Blues',
3474             1013 => 'Music|Blues|Electric Blues',
3475             1014 => "Music|Children's Music|Lullabies",
3476             1015 => "Music|Children's Music|Sing-Along",
3477             1016 => "Music|Children's Music|Stories",
3478             1017 => 'Music|Classical|Avant-Garde',
3479             1018 => 'Music|Classical|Baroque Era',
3480             1019 => 'Music|Classical|Chamber Music',
3481             1020 => 'Music|Classical|Chant',
3482             1021 => 'Music|Classical|Choral',
3483             1022 => 'Music|Classical|Classical Crossover',
3484             1023 => 'Music|Classical|Early Music',
3485             1024 => 'Music|Classical|Impressionist',
3486             1025 => 'Music|Classical|Medieval Era',
3487             1026 => 'Music|Classical|Minimalism',
3488             1027 => 'Music|Classical|Modern Era',
3489             1028 => 'Music|Classical|Opera',
3490             1029 => 'Music|Classical|Orchestral',
3491             1030 => 'Music|Classical|Renaissance',
3492             1031 => 'Music|Classical|Romantic Era',
3493             1032 => 'Music|Classical|Wedding Music',
3494             1033 => 'Music|Country|Alternative Country',
3495             1034 => 'Music|Country|Americana',
3496             1035 => 'Music|Country|Bluegrass',
3497             1036 => 'Music|Country|Contemporary Bluegrass',
3498             1037 => 'Music|Country|Contemporary Country',
3499             1038 => 'Music|Country|Country Gospel',
3500             1039 => 'Music|Country|Honky Tonk',
3501             1040 => 'Music|Country|Outlaw Country',
3502             1041 => 'Music|Country|Traditional Bluegrass',
3503             1042 => 'Music|Country|Traditional Country',
3504             1043 => 'Music|Country|Urban Cowboy',
3505             1044 => 'Music|Dance|Breakbeat',
3506             1045 => 'Music|Dance|Exercise',
3507             1046 => 'Music|Dance|Garage',
3508             1047 => 'Music|Dance|Hardcore',
3509             1048 => 'Music|Dance|House',
3510             1049 => "Music|Dance|Jungle/Drum'n'bass",
3511             1050 => 'Music|Dance|Techno',
3512             1051 => 'Music|Dance|Trance',
3513             1052 => 'Music|Jazz|Big Band',
3514             1053 => 'Music|Jazz|Bop',
3515             1054 => 'Music|Easy Listening|Lounge',
3516             1055 => 'Music|Easy Listening|Swing',
3517             1056 => 'Music|Electronic|Ambient',
3518             1057 => 'Music|Electronic|Downtempo',
3519             1058 => 'Music|Electronic|Electronica',
3520             1060 => 'Music|Electronic|IDM/Experimental',
3521             1061 => 'Music|Electronic|Industrial',
3522             1062 => 'Music|Singer/Songwriter|Alternative Folk',
3523             1063 => 'Music|Singer/Songwriter|Contemporary Folk',
3524             1064 => 'Music|Singer/Songwriter|Contemporary Singer/Songwriter',
3525             1065 => 'Music|Singer/Songwriter|Folk-Rock',
3526             1066 => 'Music|Singer/Songwriter|New Acoustic',
3527             1067 => 'Music|Singer/Songwriter|Traditional Folk',
3528             1068 => 'Music|Hip-Hop/Rap|Alternative Rap',
3529             1069 => 'Music|Hip-Hop/Rap|Dirty South',
3530             1070 => 'Music|Hip-Hop/Rap|East Coast Rap',
3531             1071 => 'Music|Hip-Hop/Rap|Gangsta Rap',
3532             1072 => 'Music|Hip-Hop/Rap|Hardcore Rap',
3533             1073 => 'Music|Hip-Hop/Rap|Hip-Hop',
3534             1074 => 'Music|Hip-Hop/Rap|Latin Rap',
3535             1075 => 'Music|Hip-Hop/Rap|Old School Rap',
3536             1076 => 'Music|Hip-Hop/Rap|Rap',
3537             1077 => 'Music|Hip-Hop/Rap|Underground Rap',
3538             1078 => 'Music|Hip-Hop/Rap|West Coast Rap',
3539             1079 => 'Music|Holiday|Chanukah',
3540             1080 => 'Music|Holiday|Christmas',
3541             1081 => "Music|Holiday|Christmas: Children's",
3542             1082 => 'Music|Holiday|Christmas: Classic',
3543             1083 => 'Music|Holiday|Christmas: Classical',
3544             1084 => 'Music|Holiday|Christmas: Jazz',
3545             1085 => 'Music|Holiday|Christmas: Modern',
3546             1086 => 'Music|Holiday|Christmas: Pop',
3547             1087 => 'Music|Holiday|Christmas: R&B',
3548             1088 => 'Music|Holiday|Christmas: Religious',
3549             1089 => 'Music|Holiday|Christmas: Rock',
3550             1090 => 'Music|Holiday|Easter',
3551             1091 => 'Music|Holiday|Halloween',
3552             1092 => 'Music|Holiday|Holiday: Other',
3553             1093 => 'Music|Holiday|Thanksgiving',
3554             1094 => 'Music|Christian & Gospel|CCM',
3555             1095 => 'Music|Christian & Gospel|Christian Metal',
3556             1096 => 'Music|Christian & Gospel|Christian Pop',
3557             1097 => 'Music|Christian & Gospel|Christian Rap',
3558             1098 => 'Music|Christian & Gospel|Christian Rock',
3559             1099 => 'Music|Christian & Gospel|Classic Christian',
3560             1100 => 'Music|Christian & Gospel|Contemporary Gospel',
3561             1101 => 'Music|Christian & Gospel|Gospel',
3562             1103 => 'Music|Christian & Gospel|Praise & Worship',
3563             1104 => 'Music|Christian & Gospel|Southern Gospel',
3564             1105 => 'Music|Christian & Gospel|Traditional Gospel',
3565             1106 => 'Music|Jazz|Avant-Garde Jazz',
3566             1107 => 'Music|Jazz|Contemporary Jazz',
3567             1108 => 'Music|Jazz|Crossover Jazz',
3568             1109 => 'Music|Jazz|Dixieland',
3569             1110 => 'Music|Jazz|Fusion',
3570             1111 => 'Music|Jazz|Latin Jazz',
3571             1112 => 'Music|Jazz|Mainstream Jazz',
3572             1113 => 'Music|Jazz|Ragtime',
3573             1114 => 'Music|Jazz|Smooth Jazz',
3574             1115 => 'Music|Latino|Latin Jazz',
3575             1116 => 'Music|Latino|Contemporary Latin',
3576             1117 => 'Music|Latino|Pop Latino',
3577             1118 => 'Music|Latino|Raices', # (Raíces)
3578             1119 => 'Music|Latino|Urbano latino',
3579             1120 => 'Music|Latino|Baladas y Boleros',
3580             1121 => 'Music|Latino|Rock y Alternativo',
3581             1122 => 'Music|Brazilian',
3582             1123 => 'Music|Latino|Musica Mexicana', # (Música Mexicana)
3583             1124 => 'Music|Latino|Musica tropical', # (Música tropical)
3584             1125 => 'Music|New Age|Environmental',
3585             1126 => 'Music|New Age|Healing',
3586             1127 => 'Music|New Age|Meditation',
3587             1128 => 'Music|New Age|Nature',
3588             1129 => 'Music|New Age|Relaxation',
3589             1130 => 'Music|New Age|Travel',
3590             1131 => 'Music|Pop|Adult Contemporary',
3591             1132 => 'Music|Pop|Britpop',
3592             1133 => 'Music|Pop|Pop/Rock',
3593             1134 => 'Music|Pop|Soft Rock',
3594             1135 => 'Music|Pop|Teen Pop',
3595             1136 => 'Music|R&B/Soul|Contemporary R&B',
3596             1137 => 'Music|R&B/Soul|Disco',
3597             1138 => 'Music|R&B/Soul|Doo Wop',
3598             1139 => 'Music|R&B/Soul|Funk',
3599             1140 => 'Music|R&B/Soul|Motown',
3600             1141 => 'Music|R&B/Soul|Neo-Soul',
3601             1142 => 'Music|R&B/Soul|Quiet Storm',
3602             1143 => 'Music|R&B/Soul|Soul',
3603             1144 => 'Music|Rock|Adult Alternative',
3604             1145 => 'Music|Rock|American Trad Rock',
3605             1146 => 'Music|Rock|Arena Rock',
3606             1147 => 'Music|Rock|Blues-Rock',
3607             1148 => 'Music|Rock|British Invasion',
3608             1149 => 'Music|Rock|Death Metal/Black Metal',
3609             1150 => 'Music|Rock|Glam Rock',
3610             1151 => 'Music|Rock|Hair Metal',
3611             1152 => 'Music|Rock|Hard Rock',
3612             1153 => 'Music|Rock|Metal',
3613             1154 => 'Music|Rock|Jam Bands',
3614             1155 => 'Music|Rock|Prog-Rock/Art Rock',
3615             1156 => 'Music|Rock|Psychedelic',
3616             1157 => 'Music|Rock|Rock & Roll',
3617             1158 => 'Music|Rock|Rockabilly',
3618             1159 => 'Music|Rock|Roots Rock',
3619             1160 => 'Music|Rock|Singer/Songwriter',
3620             1161 => 'Music|Rock|Southern Rock',
3621             1162 => 'Music|Rock|Surf',
3622             1163 => 'Music|Rock|Tex-Mex',
3623             1165 => 'Music|Soundtrack|Foreign Cinema',
3624             1166 => 'Music|Soundtrack|Musicals',
3625             1167 => 'Music|Comedy|Novelty',
3626             1168 => 'Music|Soundtrack|Original Score',
3627             1169 => 'Music|Soundtrack|Soundtrack',
3628             1171 => 'Music|Comedy|Standup Comedy',
3629             1172 => 'Music|Soundtrack|TV Soundtrack',
3630             1173 => 'Music|Vocal|Standards',
3631             1174 => 'Music|Vocal|Traditional Pop',
3632             1175 => 'Music|Jazz|Vocal Jazz',
3633             1176 => 'Music|Vocal|Vocal Pop',
3634             1177 => 'Music|African|Afro-Beat',
3635             1178 => 'Music|African|Afro-Pop',
3636             1179 => 'Music|World|Cajun',
3637             1180 => 'Music|World|Celtic',
3638             1181 => 'Music|World|Celtic Folk',
3639             1182 => 'Music|World|Contemporary Celtic',
3640             1183 => 'Music|Reggae|Modern Dancehall',
3641             1184 => 'Music|World|Drinking Songs',
3642             1185 => 'Music|Indian|Indian Pop',
3643             1186 => 'Music|World|Japanese Pop',
3644             1187 => 'Music|World|Klezmer',
3645             1188 => 'Music|World|Polka',
3646             1189 => 'Music|World|Traditional Celtic',
3647             1190 => 'Music|World|Worldbeat',
3648             1191 => 'Music|World|Zydeco',
3649             1192 => 'Music|Reggae|Roots Reggae',
3650             1193 => 'Music|Reggae|Dub',
3651             1194 => 'Music|Reggae|Ska',
3652             1195 => 'Music|World|Caribbean',
3653             1196 => 'Music|World|South America',
3654             1197 => 'Music|Arabic',
3655             1198 => 'Music|World|North America',
3656             1199 => 'Music|World|Hawaii',
3657             1200 => 'Music|World|Australia',
3658             1201 => 'Music|World|Japan',
3659             1202 => 'Music|World|France',
3660             1203 => 'Music|African',
3661             1204 => 'Music|World|Asia',
3662             1205 => 'Music|World|Europe',
3663             1206 => 'Music|World|South Africa',
3664             1207 => 'Music|Jazz|Hard Bop',
3665             1208 => 'Music|Jazz|Trad Jazz',
3666             1209 => 'Music|Jazz|Cool Jazz',
3667             1210 => 'Music|Blues|Acoustic Blues',
3668             1211 => 'Music|Classical|High Classical',
3669             1220 => 'Music|Brazilian|Axe', # (Axé)
3670             1221 => 'Music|Brazilian|Bossa Nova',
3671             1222 => 'Music|Brazilian|Choro',
3672             1223 => 'Music|Brazilian|Forro', # (Forró)
3673             1224 => 'Music|Brazilian|Frevo',
3674             1225 => 'Music|Brazilian|MPB',
3675             1226 => 'Music|Brazilian|Pagode',
3676             1227 => 'Music|Brazilian|Samba',
3677             1228 => 'Music|Brazilian|Sertanejo',
3678             1229 => 'Music|Brazilian|Baile Funk',
3679             1230 => 'Music|Alternative|Chinese Alt',
3680             1231 => 'Music|Alternative|Korean Indie',
3681             1232 => 'Music|Chinese',
3682             1233 => 'Music|Chinese|Chinese Classical',
3683             1234 => 'Music|Chinese|Chinese Flute',
3684             1235 => 'Music|Chinese|Chinese Opera',
3685             1236 => 'Music|Chinese|Chinese Orchestral',
3686             1237 => 'Music|Chinese|Chinese Regional Folk',
3687             1238 => 'Music|Chinese|Chinese Strings',
3688             1239 => 'Music|Chinese|Taiwanese Folk',
3689             1240 => 'Music|Chinese|Tibetan Native Music',
3690             1241 => 'Music|Hip-Hop/Rap|Chinese Hip-Hop',
3691             1242 => 'Music|Hip-Hop/Rap|Korean Hip-Hop',
3692             1243 => 'Music|Korean',
3693             1244 => 'Music|Korean|Korean Classical',
3694             1245 => 'Music|Korean|Korean Trad Song',
3695             1246 => 'Music|Korean|Korean Trad Instrumental',
3696             1247 => 'Music|Korean|Korean Trad Theater',
3697             1248 => 'Music|Rock|Chinese Rock',
3698             1249 => 'Music|Rock|Korean Rock',
3699             1250 => 'Music|Pop|C-Pop',
3700             1251 => 'Music|Pop|Cantopop/HK-Pop',
3701             1252 => 'Music|Pop|Korean Folk-Pop',
3702             1253 => 'Music|Pop|Mandopop',
3703             1254 => 'Music|Pop|Tai-Pop',
3704             1255 => 'Music|Pop|Malaysian Pop',
3705             1256 => 'Music|Pop|Pinoy Pop',
3706             1257 => 'Music|Pop|Original Pilipino Music',
3707             1258 => 'Music|Pop|Manilla Sound',
3708             1259 => 'Music|Pop|Indo Pop',
3709             1260 => 'Music|Pop|Thai Pop',
3710             1261 => 'Music|Vocal|Trot',
3711             1262 => 'Music|Indian',
3712             1263 => 'Music|Indian|Bollywood',
3713             1264 => 'Music|Indian|Regional Indian|Tamil',
3714             1265 => 'Music|Indian|Regional Indian|Telugu',
3715             1266 => 'Music|Indian|Regional Indian',
3716             1267 => 'Music|Indian|Devotional & Spiritual',
3717             1268 => 'Music|Indian|Sufi',
3718             1269 => 'Music|Indian|Indian Classical',
3719             1270 => 'Music|Russian|Russian Chanson',
3720             1271 => 'Music|World|Dini',
3721             1272 => 'Music|Turkish|Halk',
3722             1273 => 'Music|Turkish|Sanat',
3723             1274 => 'Music|World|Dangdut',
3724             1275 => 'Music|World|Indonesian Religious',
3725             1276 => 'Music|World|Calypso',
3726             1277 => 'Music|World|Soca',
3727             1278 => 'Music|Indian|Ghazals',
3728             1279 => 'Music|Indian|Indian Folk',
3729             1280 => 'Music|Turkish|Arabesque',
3730             1281 => 'Music|African|Afrikaans',
3731             1282 => 'Music|World|Farsi',
3732             1283 => 'Music|World|Israeli',
3733             1284 => 'Music|Arabic|Khaleeji',
3734             1285 => 'Music|Arabic|North African',
3735             1286 => 'Music|Arabic|Arabic Pop',
3736             1287 => 'Music|Arabic|Islamic',
3737             1288 => 'Music|Soundtrack|Sound Effects',
3738             1289 => 'Music|Folk',
3739             1290 => 'Music|Orchestral',
3740             1291 => 'Music|Marching',
3741             1293 => 'Music|Pop|Oldies',
3742             1294 => 'Music|Country|Thai Country',
3743             1295 => 'Music|World|Flamenco',
3744             1296 => 'Music|World|Tango',
3745             1297 => 'Music|World|Fado',
3746             1298 => 'Music|World|Iberia',
3747             1299 => 'Music|Russian',
3748             1300 => 'Music|Turkish',
3749             1301 => 'Podcasts|Arts',
3750             1302 => 'Podcasts|Society & Culture|Personal Journals',
3751             1303 => 'Podcasts|Comedy',
3752             1304 => 'Podcasts|Education',
3753             1305 => 'Podcasts|Kids & Family',
3754             1306 => 'Podcasts|Arts|Food',
3755             1307 => 'Podcasts|Health',
3756             1309 => 'Podcasts|TV & Film',
3757             1310 => 'Podcasts|Music',
3758             1311 => 'Podcasts|News & Politics',
3759             1314 => 'Podcasts|Religion & Spirituality',
3760             1315 => 'Podcasts|Science & Medicine',
3761             1316 => 'Podcasts|Sports & Recreation',
3762             1318 => 'Podcasts|Technology',
3763             1320 => 'Podcasts|Society & Culture|Places & Travel',
3764             1321 => 'Podcasts|Business',
3765             1323 => 'Podcasts|Games & Hobbies',
3766             1324 => 'Podcasts|Society & Culture',
3767             1325 => 'Podcasts|Government & Organizations',
3768             1337 => 'Music Videos|Classical|Piano',
3769             1401 => 'Podcasts|Arts|Literature',
3770             1402 => 'Podcasts|Arts|Design',
3771             1404 => 'Podcasts|Games & Hobbies|Video Games',
3772             1405 => 'Podcasts|Arts|Performing Arts',
3773             1406 => 'Podcasts|Arts|Visual Arts',
3774             1410 => 'Podcasts|Business|Careers',
3775             1412 => 'Podcasts|Business|Investing',
3776             1413 => 'Podcasts|Business|Management & Marketing',
3777             1415 => 'Podcasts|Education|K-12',
3778             1416 => 'Podcasts|Education|Higher Education',
3779             1417 => 'Podcasts|Health|Fitness & Nutrition',
3780             1420 => 'Podcasts|Health|Self-Help',
3781             1421 => 'Podcasts|Health|Sexuality',
3782             1438 => 'Podcasts|Religion & Spirituality|Buddhism',
3783             1439 => 'Podcasts|Religion & Spirituality|Christianity',
3784             1440 => 'Podcasts|Religion & Spirituality|Islam',
3785             1441 => 'Podcasts|Religion & Spirituality|Judaism',
3786             1443 => 'Podcasts|Society & Culture|Philosophy',
3787             1444 => 'Podcasts|Religion & Spirituality|Spirituality',
3788             1446 => 'Podcasts|Technology|Gadgets',
3789             1448 => 'Podcasts|Technology|Tech News',
3790             1450 => 'Podcasts|Technology|Podcasting',
3791             1454 => 'Podcasts|Games & Hobbies|Automotive',
3792             1455 => 'Podcasts|Games & Hobbies|Aviation',
3793             1456 => 'Podcasts|Sports & Recreation|Outdoor',
3794             1459 => 'Podcasts|Arts|Fashion & Beauty',
3795             1460 => 'Podcasts|Games & Hobbies|Hobbies',
3796             1461 => 'Podcasts|Games & Hobbies|Other Games',
3797             1462 => 'Podcasts|Society & Culture|History',
3798             1463 => 'Podcasts|Religion & Spirituality|Hinduism',
3799             1464 => 'Podcasts|Religion & Spirituality|Other',
3800             1465 => 'Podcasts|Sports & Recreation|Professional',
3801             1466 => 'Podcasts|Sports & Recreation|College & High School',
3802             1467 => 'Podcasts|Sports & Recreation|Amateur',
3803             1468 => 'Podcasts|Education|Educational Technology',
3804             1469 => 'Podcasts|Education|Language Courses',
3805             1470 => 'Podcasts|Education|Training',
3806             1471 => 'Podcasts|Business|Business News',
3807             1472 => 'Podcasts|Business|Shopping',
3808             1473 => 'Podcasts|Government & Organizations|National',
3809             1474 => 'Podcasts|Government & Organizations|Regional',
3810             1475 => 'Podcasts|Government & Organizations|Local',
3811             1476 => 'Podcasts|Government & Organizations|Non-Profit',
3812             1477 => 'Podcasts|Science & Medicine|Natural Sciences',
3813             1478 => 'Podcasts|Science & Medicine|Medicine',
3814             1479 => 'Podcasts|Science & Medicine|Social Sciences',
3815             1480 => 'Podcasts|Technology|Software How-To',
3816             1481 => 'Podcasts|Health|Alternative Health',
3817             1482 => 'Podcasts|Arts|Books',
3818             1483 => 'Podcasts|Fiction',
3819             1484 => 'Podcasts|Fiction|Drama',
3820             1485 => 'Podcasts|Fiction|Science Fiction',
3821             1486 => 'Podcasts|Fiction|Comedy Fiction',
3822             1487 => 'Podcasts|History',
3823             1488 => 'Podcasts|True Crime',
3824             1489 => 'Podcasts|News',
3825             1490 => 'Podcasts|News|Business News',
3826             1491 => 'Podcasts|Business|Management',
3827             1492 => 'Podcasts|Business|Marketing',
3828             1493 => 'Podcasts|Business|Entrepreneurship',
3829             1494 => 'Podcasts|Business|Non-Profit',
3830             1495 => 'Podcasts|Comedy|Improv',
3831             1496 => 'Podcasts|Comedy|Comedy Interviews',
3832             1497 => 'Podcasts|Comedy|Stand-Up',
3833             1498 => 'Podcasts|Education|Language Learning',
3834             1499 => 'Podcasts|Education|How To',
3835             1500 => 'Podcasts|Education|Self-Improvement',
3836             1501 => 'Podcasts|Education|Courses',
3837             1502 => 'Podcasts|Leisure',
3838             1503 => 'Podcasts|Leisure|Automotive',
3839             1504 => 'Podcasts|Leisure|Aviation',
3840             1505 => 'Podcasts|Leisure|Hobbies',
3841             1506 => 'Podcasts|Leisure|Crafts',
3842             1507 => 'Podcasts|Leisure|Games',
3843             1508 => 'Podcasts|Leisure|Home & Garden',
3844             1509 => 'Podcasts|Leisure|Video Games',
3845             1510 => 'Podcasts|Leisure|Animation & Manga',
3846             1511 => 'Podcasts|Government',
3847             1512 => 'Podcasts|Health & Fitness',
3848             1513 => 'Podcasts|Health & Fitness|Alternative Health',
3849             1514 => 'Podcasts|Health & Fitness|Fitness',
3850             1515 => 'Podcasts|Health & Fitness|Nutrition',
3851             1516 => 'Podcasts|Health & Fitness|Sexuality',
3852             1517 => 'Podcasts|Health & Fitness|Mental Health',
3853             1518 => 'Podcasts|Health & Fitness|Medicine',
3854             1519 => 'Podcasts|Kids & Family|Education for Kids',
3855             1520 => 'Podcasts|Kids & Family|Stories for Kids',
3856             1521 => 'Podcasts|Kids & Family|Parenting',
3857             1522 => 'Podcasts|Kids & Family|Pets & Animals',
3858             1523 => 'Podcasts|Music|Music Commentary',
3859             1524 => 'Podcasts|Music|Music History',
3860             1525 => 'Podcasts|Music|Music Interviews',
3861             1526 => 'Podcasts|News|Daily News',
3862             1527 => 'Podcasts|News|Politics',
3863             1528 => 'Podcasts|News|Tech News',
3864             1529 => 'Podcasts|News|Sports News',
3865             1530 => 'Podcasts|News|News Commentary',
3866             1531 => 'Podcasts|News|Entertainment News',
3867             1532 => 'Podcasts|Religion & Spirituality|Religion',
3868             1533 => 'Podcasts|Science',
3869             1534 => 'Podcasts|Science|Natural Sciences',
3870             1535 => 'Podcasts|Science|Social Sciences',
3871             1536 => 'Podcasts|Science|Mathematics',
3872             1537 => 'Podcasts|Science|Nature',
3873             1538 => 'Podcasts|Science|Astronomy',
3874             1539 => 'Podcasts|Science|Chemistry',
3875             1540 => 'Podcasts|Science|Earth Sciences',
3876             1541 => 'Podcasts|Science|Life Sciences',
3877             1542 => 'Podcasts|Science|Physics',
3878             1543 => 'Podcasts|Society & Culture|Documentary',
3879             1544 => 'Podcasts|Society & Culture|Relationships',
3880             1545 => 'Podcasts|Sports',
3881             1546 => 'Podcasts|Sports|Soccer',
3882             1547 => 'Podcasts|Sports|Football',
3883             1548 => 'Podcasts|Sports|Basketball',
3884             1549 => 'Podcasts|Sports|Baseball',
3885             1550 => 'Podcasts|Sports|Hockey',
3886             1551 => 'Podcasts|Sports|Running',
3887             1552 => 'Podcasts|Sports|Rugby',
3888             1553 => 'Podcasts|Sports|Golf',
3889             1554 => 'Podcasts|Sports|Cricket',
3890             1555 => 'Podcasts|Sports|Wrestling',
3891             1556 => 'Podcasts|Sports|Tennis',
3892             1557 => 'Podcasts|Sports|Volleyball',
3893             1558 => 'Podcasts|Sports|Swimming',
3894             1559 => 'Podcasts|Sports|Wilderness',
3895             1560 => 'Podcasts|Sports|Fantasy Sports',
3896             1561 => 'Podcasts|TV & Film|TV Reviews',
3897             1562 => 'Podcasts|TV & Film|After Shows',
3898             1563 => 'Podcasts|TV & Film|Film Reviews',
3899             1564 => 'Podcasts|TV & Film|Film History',
3900             1565 => 'Podcasts|TV & Film|Film Interviews',
3901             1602 => 'Music Videos|Blues',
3902             1603 => 'Music Videos|Comedy',
3903             1604 => "Music Videos|Children's Music",
3904             1605 => 'Music Videos|Classical',
3905             1606 => 'Music Videos|Country',
3906             1607 => 'Music Videos|Electronic',
3907             1608 => 'Music Videos|Holiday',
3908             1609 => 'Music Videos|Classical|Opera',
3909             1610 => 'Music Videos|Singer/Songwriter',
3910             1611 => 'Music Videos|Jazz',
3911             1612 => 'Music Videos|Latin',
3912             1613 => 'Music Videos|New Age',
3913             1614 => 'Music Videos|Pop',
3914             1615 => 'Music Videos|R&B/Soul',
3915             1616 => 'Music Videos|Soundtrack',
3916             1617 => 'Music Videos|Dance',
3917             1618 => 'Music Videos|Hip-Hop/Rap',
3918             1619 => 'Music Videos|World',
3919             1620 => 'Music Videos|Alternative',
3920             1621 => 'Music Videos|Rock',
3921             1622 => 'Music Videos|Christian & Gospel',
3922             1623 => 'Music Videos|Vocal',
3923             1624 => 'Music Videos|Reggae',
3924             1625 => 'Music Videos|Easy Listening',
3925             1626 => 'Music Videos|Podcasts',
3926             1627 => 'Music Videos|J-Pop',
3927             1628 => 'Music Videos|Enka',
3928             1629 => 'Music Videos|Anime',
3929             1630 => 'Music Videos|Kayokyoku',
3930             1631 => 'Music Videos|Disney',
3931             1632 => 'Music Videos|French Pop',
3932             1633 => 'Music Videos|German Pop',
3933             1634 => 'Music Videos|German Folk',
3934             1635 => 'Music Videos|Alternative|Chinese Alt',
3935             1636 => 'Music Videos|Alternative|Korean Indie',
3936             1637 => 'Music Videos|Chinese',
3937             1638 => 'Music Videos|Chinese|Chinese Classical',
3938             1639 => 'Music Videos|Chinese|Chinese Flute',
3939             1640 => 'Music Videos|Chinese|Chinese Opera',
3940             1641 => 'Music Videos|Chinese|Chinese Orchestral',
3941             1642 => 'Music Videos|Chinese|Chinese Regional Folk',
3942             1643 => 'Music Videos|Chinese|Chinese Strings',
3943             1644 => 'Music Videos|Chinese|Taiwanese Folk',
3944             1645 => 'Music Videos|Chinese|Tibetan Native Music',
3945             1646 => 'Music Videos|Hip-Hop/Rap|Chinese Hip-Hop',
3946             1647 => 'Music Videos|Hip-Hop/Rap|Korean Hip-Hop',
3947             1648 => 'Music Videos|Korean',
3948             1649 => 'Music Videos|Korean|Korean Classical',
3949             1650 => 'Music Videos|Korean|Korean Trad Song',
3950             1651 => 'Music Videos|Korean|Korean Trad Instrumental',
3951             1652 => 'Music Videos|Korean|Korean Trad Theater',
3952             1653 => 'Music Videos|Rock|Chinese Rock',
3953             1654 => 'Music Videos|Rock|Korean Rock',
3954             1655 => 'Music Videos|Pop|C-Pop',
3955             1656 => 'Music Videos|Pop|Cantopop/HK-Pop',
3956             1657 => 'Music Videos|Pop|Korean Folk-Pop',
3957             1658 => 'Music Videos|Pop|Mandopop',
3958             1659 => 'Music Videos|Pop|Tai-Pop',
3959             1660 => 'Music Videos|Pop|Malaysian Pop',
3960             1661 => 'Music Videos|Pop|Pinoy Pop',
3961             1662 => 'Music Videos|Pop|Original Pilipino Music',
3962             1663 => 'Music Videos|Pop|Manilla Sound',
3963             1664 => 'Music Videos|Pop|Indo Pop',
3964             1665 => 'Music Videos|Pop|Thai Pop',
3965             1666 => 'Music Videos|Vocal|Trot',
3966             1671 => 'Music Videos|Brazilian',
3967             1672 => 'Music Videos|Brazilian|Axe', # (Axé)
3968             1673 => 'Music Videos|Brazilian|Baile Funk',
3969             1674 => 'Music Videos|Brazilian|Bossa Nova',
3970             1675 => 'Music Videos|Brazilian|Choro',
3971             1676 => 'Music Videos|Brazilian|Forro',
3972             1677 => 'Music Videos|Brazilian|Frevo',
3973             1678 => 'Music Videos|Brazilian|MPB',
3974             1679 => 'Music Videos|Brazilian|Pagode',
3975             1680 => 'Music Videos|Brazilian|Samba',
3976             1681 => 'Music Videos|Brazilian|Sertanejo',
3977             1682 => 'Music Videos|Classical|High Classical',
3978             1683 => 'Music Videos|Fitness & Workout',
3979             1684 => 'Music Videos|Instrumental',
3980             1685 => 'Music Videos|Jazz|Big Band',
3981             1686 => 'Music Videos|Pop|K-Pop',
3982             1687 => 'Music Videos|Karaoke',
3983             1688 => 'Music Videos|Rock|Heavy Metal',
3984             1689 => 'Music Videos|Spoken Word',
3985             1690 => 'Music Videos|Indian',
3986             1691 => 'Music Videos|Indian|Bollywood',
3987             1692 => 'Music Videos|Indian|Regional Indian|Tamil',
3988             1693 => 'Music Videos|Indian|Regional Indian|Telugu',
3989             1694 => 'Music Videos|Indian|Regional Indian',
3990             1695 => 'Music Videos|Indian|Devotional & Spiritual',
3991             1696 => 'Music Videos|Indian|Sufi',
3992             1697 => 'Music Videos|Indian|Indian Classical',
3993             1698 => 'Music Videos|Russian|Russian Chanson',
3994             1699 => 'Music Videos|World|Dini',
3995             1700 => 'Music Videos|Turkish|Halk',
3996             1701 => 'Music Videos|Turkish|Sanat',
3997             1702 => 'Music Videos|World|Dangdut',
3998             1703 => 'Music Videos|World|Indonesian Religious',
3999             1704 => 'Music Videos|Indian|Indian Pop',
4000             1705 => 'Music Videos|World|Calypso',
4001             1706 => 'Music Videos|World|Soca',
4002             1707 => 'Music Videos|Indian|Ghazals',
4003             1708 => 'Music Videos|Indian|Indian Folk',
4004             1709 => 'Music Videos|Turkish|Arabesque',
4005             1710 => 'Music Videos|African|Afrikaans',
4006             1711 => 'Music Videos|World|Farsi',
4007             1712 => 'Music Videos|World|Israeli',
4008             1713 => 'Music Videos|Arabic',
4009             1714 => 'Music Videos|Arabic|Khaleeji',
4010             1715 => 'Music Videos|Arabic|North African',
4011             1716 => 'Music Videos|Arabic|Arabic Pop',
4012             1717 => 'Music Videos|Arabic|Islamic',
4013             1718 => 'Music Videos|Soundtrack|Sound Effects',
4014             1719 => 'Music Videos|Folk',
4015             1720 => 'Music Videos|Orchestral',
4016             1721 => 'Music Videos|Marching',
4017             1723 => 'Music Videos|Pop|Oldies',
4018             1724 => 'Music Videos|Country|Thai Country',
4019             1725 => 'Music Videos|World|Flamenco',
4020             1726 => 'Music Videos|World|Tango',
4021             1727 => 'Music Videos|World|Fado',
4022             1728 => 'Music Videos|World|Iberia',
4023             1729 => 'Music Videos|Russian',
4024             1730 => 'Music Videos|Turkish',
4025             1731 => 'Music Videos|Alternative|College Rock',
4026             1732 => 'Music Videos|Alternative|Goth Rock',
4027             1733 => 'Music Videos|Alternative|Grunge',
4028             1734 => 'Music Videos|Alternative|Indie Rock',
4029             1735 => 'Music Videos|Alternative|New Wave',
4030             1736 => 'Music Videos|Alternative|Punk',
4031             1737 => 'Music Videos|Blues|Acoustic Blues',
4032             1738 => 'Music Videos|Blues|Chicago Blues',
4033             1739 => 'Music Videos|Blues|Classic Blues',
4034             1740 => 'Music Videos|Blues|Contemporary Blues',
4035             1741 => 'Music Videos|Blues|Country Blues',
4036             1742 => 'Music Videos|Blues|Delta Blues',
4037             1743 => 'Music Videos|Blues|Electric Blues',
4038             1744 => "Music Videos|Children's Music|Lullabies",
4039             1745 => "Music Videos|Children's Music|Sing-Along",
4040             1746 => "Music Videos|Children's Music|Stories",
4041             1747 => 'Music Videos|Christian & Gospel|CCM',
4042             1748 => 'Music Videos|Christian & Gospel|Christian Metal',
4043             1749 => 'Music Videos|Christian & Gospel|Christian Pop',
4044             1750 => 'Music Videos|Christian & Gospel|Christian Rap',
4045             1751 => 'Music Videos|Christian & Gospel|Christian Rock',
4046             1752 => 'Music Videos|Christian & Gospel|Classic Christian',
4047             1753 => 'Music Videos|Christian & Gospel|Contemporary Gospel',
4048             1754 => 'Music Videos|Christian & Gospel|Gospel',
4049             1755 => 'Music Videos|Christian & Gospel|Praise & Worship',
4050             1756 => 'Music Videos|Christian & Gospel|Southern Gospel',
4051             1757 => 'Music Videos|Christian & Gospel|Traditional Gospel',
4052             1758 => 'Music Videos|Classical|Avant-Garde',
4053             1759 => 'Music Videos|Classical|Baroque Era',
4054             1760 => 'Music Videos|Classical|Chamber Music',
4055             1761 => 'Music Videos|Classical|Chant',
4056             1762 => 'Music Videos|Classical|Choral',
4057             1763 => 'Music Videos|Classical|Classical Crossover',
4058             1764 => 'Music Videos|Classical|Early Music',
4059             1765 => 'Music Videos|Classical|Impressionist',
4060             1766 => 'Music Videos|Classical|Medieval Era',
4061             1767 => 'Music Videos|Classical|Minimalism',
4062             1768 => 'Music Videos|Classical|Modern Era',
4063             1769 => 'Music Videos|Classical|Orchestral',
4064             1770 => 'Music Videos|Classical|Renaissance',
4065             1771 => 'Music Videos|Classical|Romantic Era',
4066             1772 => 'Music Videos|Classical|Wedding Music',
4067             1773 => 'Music Videos|Comedy|Novelty',
4068             1774 => 'Music Videos|Comedy|Standup Comedy',
4069             1775 => 'Music Videos|Country|Alternative Country',
4070             1776 => 'Music Videos|Country|Americana',
4071             1777 => 'Music Videos|Country|Bluegrass',
4072             1778 => 'Music Videos|Country|Contemporary Bluegrass',
4073             1779 => 'Music Videos|Country|Contemporary Country',
4074             1780 => 'Music Videos|Country|Country Gospel',
4075             1781 => 'Music Videos|Country|Honky Tonk',
4076             1782 => 'Music Videos|Country|Outlaw Country',
4077             1783 => 'Music Videos|Country|Traditional Bluegrass',
4078             1784 => 'Music Videos|Country|Traditional Country',
4079             1785 => 'Music Videos|Country|Urban Cowboy',
4080             1786 => 'Music Videos|Dance|Breakbeat',
4081             1787 => 'Music Videos|Dance|Exercise',
4082             1788 => 'Music Videos|Dance|Garage',
4083             1789 => 'Music Videos|Dance|Hardcore',
4084             1790 => 'Music Videos|Dance|House',
4085             1791 => "Music Videos|Dance|Jungle/Drum'n'bass",
4086             1792 => 'Music Videos|Dance|Techno',
4087             1793 => 'Music Videos|Dance|Trance',
4088             1794 => 'Music Videos|Easy Listening|Lounge',
4089             1795 => 'Music Videos|Easy Listening|Swing',
4090             1796 => 'Music Videos|Electronic|Ambient',
4091             1797 => 'Music Videos|Electronic|Downtempo',
4092             1798 => 'Music Videos|Electronic|Electronica',
4093             1799 => 'Music Videos|Electronic|IDM/Experimental',
4094             1800 => 'Music Videos|Electronic|Industrial',
4095             1801 => 'Music Videos|Hip-Hop/Rap|Alternative Rap',
4096             1802 => 'Music Videos|Hip-Hop/Rap|Dirty South',
4097             1803 => 'Music Videos|Hip-Hop/Rap|East Coast Rap',
4098             1804 => 'Music Videos|Hip-Hop/Rap|Gangsta Rap',
4099             1805 => 'Music Videos|Hip-Hop/Rap|Hardcore Rap',
4100             1806 => 'Music Videos|Hip-Hop/Rap|Hip-Hop',
4101             1807 => 'Music Videos|Hip-Hop/Rap|Latin Rap',
4102             1808 => 'Music Videos|Hip-Hop/Rap|Old School Rap',
4103             1809 => 'Music Videos|Hip-Hop/Rap|Rap',
4104             1810 => 'Music Videos|Hip-Hop/Rap|Underground Rap',
4105             1811 => 'Music Videos|Hip-Hop/Rap|West Coast Rap',
4106             1812 => 'Music Videos|Holiday|Chanukah',
4107             1813 => 'Music Videos|Holiday|Christmas',
4108             1814 => "Music Videos|Holiday|Christmas: Children's",
4109             1815 => 'Music Videos|Holiday|Christmas: Classic',
4110             1816 => 'Music Videos|Holiday|Christmas: Classical',
4111             1817 => 'Music Videos|Holiday|Christmas: Jazz',
4112             1818 => 'Music Videos|Holiday|Christmas: Modern',
4113             1819 => 'Music Videos|Holiday|Christmas: Pop',
4114             1820 => 'Music Videos|Holiday|Christmas: R&B',
4115             1821 => 'Music Videos|Holiday|Christmas: Religious',
4116             1822 => 'Music Videos|Holiday|Christmas: Rock',
4117             1823 => 'Music Videos|Holiday|Easter',
4118             1824 => 'Music Videos|Holiday|Halloween',
4119             1825 => 'Music Videos|Holiday|Thanksgiving',
4120             1826 => 'Music Videos|Jazz|Avant-Garde Jazz',
4121             1828 => 'Music Videos|Jazz|Bop',
4122             1829 => 'Music Videos|Jazz|Contemporary Jazz',
4123             1830 => 'Music Videos|Jazz|Cool Jazz',
4124             1831 => 'Music Videos|Jazz|Crossover Jazz',
4125             1832 => 'Music Videos|Jazz|Dixieland',
4126             1833 => 'Music Videos|Jazz|Fusion',
4127             1834 => 'Music Videos|Jazz|Hard Bop',
4128             1835 => 'Music Videos|Jazz|Latin Jazz',
4129             1836 => 'Music Videos|Jazz|Mainstream Jazz',
4130             1837 => 'Music Videos|Jazz|Ragtime',
4131             1838 => 'Music Videos|Jazz|Smooth Jazz',
4132             1839 => 'Music Videos|Jazz|Trad Jazz',
4133             1840 => 'Music Videos|Latin|Alternative & Rock in Spanish',
4134             1841 => 'Music Videos|Latin|Baladas y Boleros',
4135             1842 => 'Music Videos|Latin|Contemporary Latin',
4136             1843 => 'Music Videos|Latin|Latin Jazz',
4137             1844 => 'Music Videos|Latin|Latin Urban',
4138             1845 => 'Music Videos|Latin|Pop in Spanish',
4139             1846 => 'Music Videos|Latin|Raices',
4140             1847 => 'Music Videos|Latin|Musica Mexicana', # (Música Mexicana)
4141             1848 => 'Music Videos|Latin|Salsa y Tropical',
4142             1849 => 'Music Videos|New Age|Healing',
4143             1850 => 'Music Videos|New Age|Meditation',
4144             1851 => 'Music Videos|New Age|Nature',
4145             1852 => 'Music Videos|New Age|Relaxation',
4146             1853 => 'Music Videos|New Age|Travel',
4147             1854 => 'Music Videos|Pop|Adult Contemporary',
4148             1855 => 'Music Videos|Pop|Britpop',
4149             1856 => 'Music Videos|Pop|Pop/Rock',
4150             1857 => 'Music Videos|Pop|Soft Rock',
4151             1858 => 'Music Videos|Pop|Teen Pop',
4152             1859 => 'Music Videos|R&B/Soul|Contemporary R&B',
4153             1860 => 'Music Videos|R&B/Soul|Disco',
4154             1861 => 'Music Videos|R&B/Soul|Doo Wop',
4155             1862 => 'Music Videos|R&B/Soul|Funk',
4156             1863 => 'Music Videos|R&B/Soul|Motown',
4157             1864 => 'Music Videos|R&B/Soul|Neo-Soul',
4158             1865 => 'Music Videos|R&B/Soul|Soul',
4159             1866 => 'Music Videos|Reggae|Modern Dancehall',
4160             1867 => 'Music Videos|Reggae|Dub',
4161             1868 => 'Music Videos|Reggae|Roots Reggae',
4162             1869 => 'Music Videos|Reggae|Ska',
4163             1870 => 'Music Videos|Rock|Adult Alternative',
4164             1871 => 'Music Videos|Rock|American Trad Rock',
4165             1872 => 'Music Videos|Rock|Arena Rock',
4166             1873 => 'Music Videos|Rock|Blues-Rock',
4167             1874 => 'Music Videos|Rock|British Invasion',
4168             1875 => 'Music Videos|Rock|Death Metal/Black Metal',
4169             1876 => 'Music Videos|Rock|Glam Rock',
4170             1877 => 'Music Videos|Rock|Hair Metal',
4171             1878 => 'Music Videos|Rock|Hard Rock',
4172             1879 => 'Music Videos|Rock|Jam Bands',
4173             1880 => 'Music Videos|Rock|Prog-Rock/Art Rock',
4174             1881 => 'Music Videos|Rock|Psychedelic',
4175             1882 => 'Music Videos|Rock|Rock & Roll',
4176             1883 => 'Music Videos|Rock|Rockabilly',
4177             1884 => 'Music Videos|Rock|Roots Rock',
4178             1885 => 'Music Videos|Rock|Singer/Songwriter',
4179             1886 => 'Music Videos|Rock|Southern Rock',
4180             1887 => 'Music Videos|Rock|Surf',
4181             1888 => 'Music Videos|Rock|Tex-Mex',
4182             1889 => 'Music Videos|Singer/Songwriter|Alternative Folk',
4183             1890 => 'Music Videos|Singer/Songwriter|Contemporary Folk',
4184             1891 => 'Music Videos|Singer/Songwriter|Contemporary Singer/Songwriter',
4185             1892 => 'Music Videos|Singer/Songwriter|Folk-Rock',
4186             1893 => 'Music Videos|Singer/Songwriter|New Acoustic',
4187             1894 => 'Music Videos|Singer/Songwriter|Traditional Folk',
4188             1895 => 'Music Videos|Soundtrack|Foreign Cinema',
4189             1896 => 'Music Videos|Soundtrack|Musicals',
4190             1897 => 'Music Videos|Soundtrack|Original Score',
4191             1898 => 'Music Videos|Soundtrack|Soundtrack',
4192             1899 => 'Music Videos|Soundtrack|TV Soundtrack',
4193             1900 => 'Music Videos|Vocal|Standards',
4194             1901 => 'Music Videos|Vocal|Traditional Pop',
4195             1902 => 'Music Videos|Jazz|Vocal Jazz',
4196             1903 => 'Music Videos|Vocal|Vocal Pop',
4197             1904 => 'Music Videos|African',
4198             1905 => 'Music Videos|African|Afro-Beat',
4199             1906 => 'Music Videos|African|Afro-Pop',
4200             1907 => 'Music Videos|World|Asia',
4201             1908 => 'Music Videos|World|Australia',
4202             1909 => 'Music Videos|World|Cajun',
4203             1910 => 'Music Videos|World|Caribbean',
4204             1911 => 'Music Videos|World|Celtic',
4205             1912 => 'Music Videos|World|Celtic Folk',
4206             1913 => 'Music Videos|World|Contemporary Celtic',
4207             1914 => 'Music Videos|World|Europe',
4208             1915 => 'Music Videos|World|France',
4209             1916 => 'Music Videos|World|Hawaii',
4210             1917 => 'Music Videos|World|Japan',
4211             1918 => 'Music Videos|World|Klezmer',
4212             1919 => 'Music Videos|World|North America',
4213             1920 => 'Music Videos|World|Polka',
4214             1921 => 'Music Videos|World|South Africa',
4215             1922 => 'Music Videos|World|South America',
4216             1923 => 'Music Videos|World|Traditional Celtic',
4217             1924 => 'Music Videos|World|Worldbeat',
4218             1925 => 'Music Videos|World|Zydeco',
4219             1926 => 'Music Videos|Christian & Gospel',
4220             1928 => 'Music Videos|Classical|Art Song',
4221             1929 => 'Music Videos|Classical|Brass & Woodwinds',
4222             1930 => 'Music Videos|Classical|Solo Instrumental',
4223             1931 => 'Music Videos|Classical|Contemporary Era',
4224             1932 => 'Music Videos|Classical|Oratorio',
4225             1933 => 'Music Videos|Classical|Cantata',
4226             1934 => 'Music Videos|Classical|Electronic',
4227             1935 => 'Music Videos|Classical|Sacred',
4228             1936 => 'Music Videos|Classical|Guitar',
4229             1938 => 'Music Videos|Classical|Violin',
4230             1939 => 'Music Videos|Classical|Cello',
4231             1940 => 'Music Videos|Classical|Percussion',
4232             1941 => 'Music Videos|Electronic|Dubstep',
4233             1942 => 'Music Videos|Electronic|Bass',
4234             1943 => 'Music Videos|Hip-Hop/Rap|UK Hip-Hop',
4235             1944 => 'Music Videos|Reggae|Lovers Rock',
4236             1945 => 'Music Videos|Alternative|EMO',
4237             1946 => 'Music Videos|Alternative|Pop Punk',
4238             1947 => 'Music Videos|Alternative|Indie Pop',
4239             1948 => 'Music Videos|New Age|Yoga',
4240             1949 => 'Music Videos|Pop|Tribute',
4241             1950 => 'Music Videos|Pop|Shows',
4242             1951 => 'Music Videos|Cuban',
4243             1952 => 'Music Videos|Cuban|Mambo',
4244             1953 => 'Music Videos|Cuban|Chachacha',
4245             1954 => 'Music Videos|Cuban|Guajira',
4246             1955 => 'Music Videos|Cuban|Son',
4247             1956 => 'Music Videos|Cuban|Bolero',
4248             1957 => 'Music Videos|Cuban|Guaracha',
4249             1958 => 'Music Videos|Cuban|Timba',
4250             1959 => 'Music Videos|Soundtrack|Video Game',
4251             1960 => 'Music Videos|Indian|Regional Indian|Punjabi|Punjabi Pop',
4252             1961 => 'Music Videos|Indian|Regional Indian|Bengali|Rabindra Sangeet',
4253             1962 => 'Music Videos|Indian|Regional Indian|Malayalam',
4254             1963 => 'Music Videos|Indian|Regional Indian|Kannada',
4255             1964 => 'Music Videos|Indian|Regional Indian|Marathi',
4256             1965 => 'Music Videos|Indian|Regional Indian|Gujarati',
4257             1966 => 'Music Videos|Indian|Regional Indian|Assamese',
4258             1967 => 'Music Videos|Indian|Regional Indian|Bhojpuri',
4259             1968 => 'Music Videos|Indian|Regional Indian|Haryanvi',
4260             1969 => 'Music Videos|Indian|Regional Indian|Odia',
4261             1970 => 'Music Videos|Indian|Regional Indian|Rajasthani',
4262             1971 => 'Music Videos|Indian|Regional Indian|Urdu',
4263             1972 => 'Music Videos|Indian|Regional Indian|Punjabi',
4264             1973 => 'Music Videos|Indian|Regional Indian|Bengali',
4265             1974 => 'Music Videos|Indian|Indian Classical|Carnatic Classical',
4266             1975 => 'Music Videos|Indian|Indian Classical|Hindustani Classical',
4267             1976 => 'Music Videos|African|Afro House',
4268             1977 => 'Music Videos|African|Afro Soul',
4269             1978 => 'Music Videos|African|Afrobeats',
4270             1979 => 'Music Videos|African|Benga',
4271             1980 => 'Music Videos|African|Bongo-Flava',
4272             1981 => 'Music Videos|African|Coupe-Decale',
4273             1982 => 'Music Videos|African|Gqom',
4274             1983 => 'Music Videos|African|Highlife',
4275             1984 => 'Music Videos|African|Kuduro',
4276             1985 => 'Music Videos|African|Kizomba',
4277             1986 => 'Music Videos|African|Kwaito',
4278             1987 => 'Music Videos|African|Mbalax',
4279             1988 => 'Music Videos|African|Ndombolo',
4280             1989 => 'Music Videos|African|Shangaan Electro',
4281             1990 => 'Music Videos|African|Soukous',
4282             1991 => 'Music Videos|African|Taarab',
4283             1992 => 'Music Videos|African|Zouglou',
4284             1993 => 'Music Videos|Turkish|Ozgun',
4285             1994 => 'Music Videos|Turkish|Fantezi',
4286             1995 => 'Music Videos|Turkish|Religious',
4287             1996 => 'Music Videos|Pop|Turkish Pop',
4288             1997 => 'Music Videos|Rock|Turkish Rock',
4289             1998 => 'Music Videos|Alternative|Turkish Alternative',
4290             1999 => 'Music Videos|Hip-Hop/Rap|Turkish Hip-Hop/Rap',
4291             2000 => 'Music Videos|African|Maskandi',
4292             2001 => 'Music Videos|Russian|Russian Romance',
4293             2002 => 'Music Videos|Russian|Russian Bard',
4294             2003 => 'Music Videos|Russian|Russian Pop',
4295             2004 => 'Music Videos|Russian|Russian Rock',
4296             2005 => 'Music Videos|Russian|Russian Hip-Hop',
4297             2006 => 'Music Videos|Arabic|Levant',
4298             2007 => 'Music Videos|Arabic|Levant|Dabke',
4299             2008 => 'Music Videos|Arabic|Maghreb Rai',
4300             2009 => 'Music Videos|Arabic|Khaleeji|Khaleeji Jalsat',
4301             2010 => 'Music Videos|Arabic|Khaleeji|Khaleeji Shailat',
4302             2011 => 'Music Videos|Tarab',
4303             2012 => 'Music Videos|Tarab|Iraqi Tarab',
4304             2013 => 'Music Videos|Tarab|Egyptian Tarab',
4305             2014 => 'Music Videos|Tarab|Khaleeji Tarab',
4306             2015 => 'Music Videos|Pop|Levant Pop',
4307             2016 => 'Music Videos|Pop|Iraqi Pop',
4308             2017 => 'Music Videos|Pop|Egyptian Pop',
4309             2018 => 'Music Videos|Pop|Maghreb Pop',
4310             2019 => 'Music Videos|Pop|Khaleeji Pop',
4311             2020 => 'Music Videos|Hip-Hop/Rap|Levant Hip-Hop',
4312             2021 => 'Music Videos|Hip-Hop/Rap|Egyptian Hip-Hop',
4313             2022 => 'Music Videos|Hip-Hop/Rap|Maghreb Hip-Hop',
4314             2023 => 'Music Videos|Hip-Hop/Rap|Khaleeji Hip-Hop',
4315             2024 => 'Music Videos|Alternative|Indie Levant',
4316             2025 => 'Music Videos|Alternative|Indie Egyptian',
4317             2026 => 'Music Videos|Alternative|Indie Maghreb',
4318             2027 => 'Music Videos|Electronic|Levant Electronic',
4319             2028 => "Music Videos|Electronic|Electro-Cha'abi",
4320             2029 => 'Music Videos|Electronic|Maghreb Electronic',
4321             2030 => 'Music Videos|Folk|Iraqi Folk',
4322             2031 => 'Music Videos|Folk|Khaleeji Folk',
4323             2032 => 'Music Videos|Dance|Maghreb Dance',
4324             4000 => 'TV Shows|Comedy',
4325             4001 => 'TV Shows|Drama',
4326             4002 => 'TV Shows|Animation',
4327             4003 => 'TV Shows|Action & Adventure',
4328             4004 => 'TV Shows|Classics',
4329             4005 => 'TV Shows|Kids & Family',
4330             4006 => 'TV Shows|Nonfiction',
4331             4007 => 'TV Shows|Reality TV',
4332             4008 => 'TV Shows|Sci-Fi & Fantasy',
4333             4009 => 'TV Shows|Sports',
4334             4010 => 'TV Shows|Teens',
4335             4011 => 'TV Shows|Latino TV',
4336             4401 => 'Movies|Action & Adventure',
4337             4402 => 'Movies|Anime',
4338             4403 => 'Movies|Classics',
4339             4404 => 'Movies|Comedy',
4340             4405 => 'Movies|Documentary',
4341             4406 => 'Movies|Drama',
4342             4407 => 'Movies|Foreign',
4343             4408 => 'Movies|Horror',
4344             4409 => 'Movies|Independent',
4345             4410 => 'Movies|Kids & Family',
4346             4411 => 'Movies|Musicals',
4347             4412 => 'Movies|Romance',
4348             4413 => 'Movies|Sci-Fi & Fantasy',
4349             4414 => 'Movies|Short Films',
4350             4415 => 'Movies|Special Interest',
4351             4416 => 'Movies|Thriller',
4352             4417 => 'Movies|Sports',
4353             4418 => 'Movies|Western',
4354             4419 => 'Movies|Urban',
4355             4420 => 'Movies|Holiday',
4356             4421 => 'Movies|Made for TV',
4357             4422 => 'Movies|Concert Films',
4358             4423 => 'Movies|Music Documentaries',
4359             4424 => 'Movies|Music Feature Films',
4360             4425 => 'Movies|Japanese Cinema',
4361             4426 => 'Movies|Jidaigeki',
4362             4427 => 'Movies|Tokusatsu',
4363             4428 => 'Movies|Korean Cinema',
4364             4429 => 'Movies|Russian',
4365             4430 => 'Movies|Turkish',
4366             4431 => 'Movies|Bollywood',
4367             4432 => 'Movies|Regional Indian',
4368             4433 => 'Movies|Middle Eastern',
4369             4434 => 'Movies|African',
4370             6000 => 'App Store|Business',
4371             6001 => 'App Store|Weather',
4372             6002 => 'App Store|Utilities',
4373             6003 => 'App Store|Travel',
4374             6004 => 'App Store|Sports',
4375             6005 => 'App Store|Social Networking',
4376             6006 => 'App Store|Reference',
4377             6007 => 'App Store|Productivity',
4378             6008 => 'App Store|Photo & Video',
4379             6009 => 'App Store|News',
4380             6010 => 'App Store|Navigation',
4381             6011 => 'App Store|Music',
4382             6012 => 'App Store|Lifestyle',
4383             6013 => 'App Store|Health & Fitness',
4384             6014 => 'App Store|Games',
4385             6015 => 'App Store|Finance',
4386             6016 => 'App Store|Entertainment',
4387             6017 => 'App Store|Education',
4388             6018 => 'App Store|Books',
4389             6020 => 'App Store|Medical',
4390             6021 => 'App Store|Magazines & Newspapers',
4391             6022 => 'App Store|Catalogs',
4392             6023 => 'App Store|Food & Drink',
4393             6024 => 'App Store|Shopping',
4394             6025 => 'App Store|Stickers',
4395             6026 => 'App Store|Developer Tools',
4396             6027 => 'App Store|Graphics & Design',
4397             7001 => 'App Store|Games|Action',
4398             7002 => 'App Store|Games|Adventure',
4399             7003 => 'App Store|Games|Casual',
4400             7004 => 'App Store|Games|Board',
4401             7005 => 'App Store|Games|Card',
4402             7006 => 'App Store|Games|Casino',
4403             7007 => 'App Store|Games|Dice',
4404             7008 => 'App Store|Games|Educational',
4405             7009 => 'App Store|Games|Family',
4406             7011 => 'App Store|Games|Music',
4407             7012 => 'App Store|Games|Puzzle',
4408             7013 => 'App Store|Games|Racing',
4409             7014 => 'App Store|Games|Role Playing',
4410             7015 => 'App Store|Games|Simulation',
4411             7016 => 'App Store|Games|Sports',
4412             7017 => 'App Store|Games|Strategy',
4413             7018 => 'App Store|Games|Trivia',
4414             7019 => 'App Store|Games|Word',
4415             8001 => 'Tones|Ringtones|Alternative',
4416             8002 => 'Tones|Ringtones|Blues',
4417             8003 => "Tones|Ringtones|Children's Music",
4418             8004 => 'Tones|Ringtones|Classical',
4419             8005 => 'Tones|Ringtones|Comedy',
4420             8006 => 'Tones|Ringtones|Country',
4421             8007 => 'Tones|Ringtones|Dance',
4422             8008 => 'Tones|Ringtones|Electronic',
4423             8009 => 'Tones|Ringtones|Enka',
4424             8010 => 'Tones|Ringtones|French Pop',
4425             8011 => 'Tones|Ringtones|German Folk',
4426             8012 => 'Tones|Ringtones|German Pop',
4427             8013 => 'Tones|Ringtones|Hip-Hop/Rap',
4428             8014 => 'Tones|Ringtones|Holiday',
4429             8015 => 'Tones|Ringtones|Inspirational',
4430             8016 => 'Tones|Ringtones|J-Pop',
4431             8017 => 'Tones|Ringtones|Jazz',
4432             8018 => 'Tones|Ringtones|Kayokyoku',
4433             8019 => 'Tones|Ringtones|Latin',
4434             8020 => 'Tones|Ringtones|New Age',
4435             8021 => 'Tones|Ringtones|Classical|Opera',
4436             8022 => 'Tones|Ringtones|Pop',
4437             8023 => 'Tones|Ringtones|R&B/Soul',
4438             8024 => 'Tones|Ringtones|Reggae',
4439             8025 => 'Tones|Ringtones|Rock',
4440             8026 => 'Tones|Ringtones|Singer/Songwriter',
4441             8027 => 'Tones|Ringtones|Soundtrack',
4442             8028 => 'Tones|Ringtones|Spoken Word',
4443             8029 => 'Tones|Ringtones|Vocal',
4444             8030 => 'Tones|Ringtones|World',
4445             8050 => 'Tones|Alert Tones|Sound Effects',
4446             8051 => 'Tones|Alert Tones|Dialogue',
4447             8052 => 'Tones|Alert Tones|Music',
4448             8053 => 'Tones|Ringtones',
4449             8054 => 'Tones|Alert Tones',
4450             8055 => 'Tones|Ringtones|Alternative|Chinese Alt',
4451             8056 => 'Tones|Ringtones|Alternative|College Rock',
4452             8057 => 'Tones|Ringtones|Alternative|Goth Rock',
4453             8058 => 'Tones|Ringtones|Alternative|Grunge',
4454             8059 => 'Tones|Ringtones|Alternative|Indie Rock',
4455             8060 => 'Tones|Ringtones|Alternative|Korean Indie',
4456             8061 => 'Tones|Ringtones|Alternative|New Wave',
4457             8062 => 'Tones|Ringtones|Alternative|Punk',
4458             8063 => 'Tones|Ringtones|Anime',
4459             8064 => 'Tones|Ringtones|Arabic',
4460             8065 => 'Tones|Ringtones|Arabic|Arabic Pop',
4461             8066 => 'Tones|Ringtones|Arabic|Islamic',
4462             8067 => 'Tones|Ringtones|Arabic|Khaleeji',
4463             8068 => 'Tones|Ringtones|Arabic|North African',
4464             8069 => 'Tones|Ringtones|Blues|Acoustic Blues',
4465             8070 => 'Tones|Ringtones|Blues|Chicago Blues',
4466             8071 => 'Tones|Ringtones|Blues|Classic Blues',
4467             8072 => 'Tones|Ringtones|Blues|Contemporary Blues',
4468             8073 => 'Tones|Ringtones|Blues|Country Blues',
4469             8074 => 'Tones|Ringtones|Blues|Delta Blues',
4470             8075 => 'Tones|Ringtones|Blues|Electric Blues',
4471             8076 => 'Tones|Ringtones|Brazilian',
4472             8077 => 'Tones|Ringtones|Brazilian|Axe', # (Axé)
4473             8078 => 'Tones|Ringtones|Brazilian|Baile Funk',
4474             8079 => 'Tones|Ringtones|Brazilian|Bossa Nova',
4475             8080 => 'Tones|Ringtones|Brazilian|Choro',
4476             8081 => 'Tones|Ringtones|Brazilian|Forro', # (Forró)
4477             8082 => 'Tones|Ringtones|Brazilian|Frevo',
4478             8083 => 'Tones|Ringtones|Brazilian|MPB',
4479             8084 => 'Tones|Ringtones|Brazilian|Pagode',
4480             8085 => 'Tones|Ringtones|Brazilian|Samba',
4481             8086 => 'Tones|Ringtones|Brazilian|Sertanejo',
4482             8087 => "Tones|Ringtones|Children's Music|Lullabies",
4483             8088 => "Tones|Ringtones|Children's Music|Sing-Along",
4484             8089 => "Tones|Ringtones|Children's Music|Stories",
4485             8090 => 'Tones|Ringtones|Chinese',
4486             8091 => 'Tones|Ringtones|Chinese|Chinese Classical',
4487             8092 => 'Tones|Ringtones|Chinese|Chinese Flute',
4488             8093 => 'Tones|Ringtones|Chinese|Chinese Opera',
4489             8094 => 'Tones|Ringtones|Chinese|Chinese Orchestral',
4490             8095 => 'Tones|Ringtones|Chinese|Chinese Regional Folk',
4491             8096 => 'Tones|Ringtones|Chinese|Chinese Strings',
4492             8097 => 'Tones|Ringtones|Chinese|Taiwanese Folk',
4493             8098 => 'Tones|Ringtones|Chinese|Tibetan Native Music',
4494             8099 => 'Tones|Ringtones|Christian & Gospel',
4495             8100 => 'Tones|Ringtones|Christian & Gospel|CCM',
4496             8101 => 'Tones|Ringtones|Christian & Gospel|Christian Metal',
4497             8102 => 'Tones|Ringtones|Christian & Gospel|Christian Pop',
4498             8103 => 'Tones|Ringtones|Christian & Gospel|Christian Rap',
4499             8104 => 'Tones|Ringtones|Christian & Gospel|Christian Rock',
4500             8105 => 'Tones|Ringtones|Christian & Gospel|Classic Christian',
4501             8106 => 'Tones|Ringtones|Christian & Gospel|Contemporary Gospel',
4502             8107 => 'Tones|Ringtones|Christian & Gospel|Gospel',
4503             8108 => 'Tones|Ringtones|Christian & Gospel|Praise & Worship',
4504             8109 => 'Tones|Ringtones|Christian & Gospel|Southern Gospel',
4505             8110 => 'Tones|Ringtones|Christian & Gospel|Traditional Gospel',
4506             8111 => 'Tones|Ringtones|Classical|Avant-Garde',
4507             8112 => 'Tones|Ringtones|Classical|Baroque Era',
4508             8113 => 'Tones|Ringtones|Classical|Chamber Music',
4509             8114 => 'Tones|Ringtones|Classical|Chant',
4510             8115 => 'Tones|Ringtones|Classical|Choral',
4511             8116 => 'Tones|Ringtones|Classical|Classical Crossover',
4512             8117 => 'Tones|Ringtones|Classical|Early Music',
4513             8118 => 'Tones|Ringtones|Classical|High Classical',
4514             8119 => 'Tones|Ringtones|Classical|Impressionist',
4515             8120 => 'Tones|Ringtones|Classical|Medieval Era',
4516             8121 => 'Tones|Ringtones|Classical|Minimalism',
4517             8122 => 'Tones|Ringtones|Classical|Modern Era',
4518             8123 => 'Tones|Ringtones|Classical|Orchestral',
4519             8124 => 'Tones|Ringtones|Classical|Renaissance',
4520             8125 => 'Tones|Ringtones|Classical|Romantic Era',
4521             8126 => 'Tones|Ringtones|Classical|Wedding Music',
4522             8127 => 'Tones|Ringtones|Comedy|Novelty',
4523             8128 => 'Tones|Ringtones|Comedy|Standup Comedy',
4524             8129 => 'Tones|Ringtones|Country|Alternative Country',
4525             8130 => 'Tones|Ringtones|Country|Americana',
4526             8131 => 'Tones|Ringtones|Country|Bluegrass',
4527             8132 => 'Tones|Ringtones|Country|Contemporary Bluegrass',
4528             8133 => 'Tones|Ringtones|Country|Contemporary Country',
4529             8134 => 'Tones|Ringtones|Country|Country Gospel',
4530             8135 => 'Tones|Ringtones|Country|Honky Tonk',
4531             8136 => 'Tones|Ringtones|Country|Outlaw Country',
4532             8137 => 'Tones|Ringtones|Country|Thai Country',
4533             8138 => 'Tones|Ringtones|Country|Traditional Bluegrass',
4534             8139 => 'Tones|Ringtones|Country|Traditional Country',
4535             8140 => 'Tones|Ringtones|Country|Urban Cowboy',
4536             8141 => 'Tones|Ringtones|Dance|Breakbeat',
4537             8142 => 'Tones|Ringtones|Dance|Exercise',
4538             8143 => 'Tones|Ringtones|Dance|Garage',
4539             8144 => 'Tones|Ringtones|Dance|Hardcore',
4540             8145 => 'Tones|Ringtones|Dance|House',
4541             8146 => "Tones|Ringtones|Dance|Jungle/Drum'n'bass",
4542             8147 => 'Tones|Ringtones|Dance|Techno',
4543             8148 => 'Tones|Ringtones|Dance|Trance',
4544             8149 => 'Tones|Ringtones|Disney',
4545             8150 => 'Tones|Ringtones|Easy Listening',
4546             8151 => 'Tones|Ringtones|Easy Listening|Lounge',
4547             8152 => 'Tones|Ringtones|Easy Listening|Swing',
4548             8153 => 'Tones|Ringtones|Electronic|Ambient',
4549             8154 => 'Tones|Ringtones|Electronic|Downtempo',
4550             8155 => 'Tones|Ringtones|Electronic|Electronica',
4551             8156 => 'Tones|Ringtones|Electronic|IDM/Experimental',
4552             8157 => 'Tones|Ringtones|Electronic|Industrial',
4553             8158 => 'Tones|Ringtones|Fitness & Workout',
4554             8159 => 'Tones|Ringtones|Folk',
4555             8160 => 'Tones|Ringtones|Hip-Hop/Rap|Alternative Rap',
4556             8161 => 'Tones|Ringtones|Hip-Hop/Rap|Chinese Hip-Hop',
4557             8162 => 'Tones|Ringtones|Hip-Hop/Rap|Dirty South',
4558             8163 => 'Tones|Ringtones|Hip-Hop/Rap|East Coast Rap',
4559             8164 => 'Tones|Ringtones|Hip-Hop/Rap|Gangsta Rap',
4560             8165 => 'Tones|Ringtones|Hip-Hop/Rap|Hardcore Rap',
4561             8166 => 'Tones|Ringtones|Hip-Hop/Rap|Hip-Hop',
4562             8167 => 'Tones|Ringtones|Hip-Hop/Rap|Korean Hip-Hop',
4563             8168 => 'Tones|Ringtones|Hip-Hop/Rap|Latin Rap',
4564             8169 => 'Tones|Ringtones|Hip-Hop/Rap|Old School Rap',
4565             8170 => 'Tones|Ringtones|Hip-Hop/Rap|Rap',
4566             8171 => 'Tones|Ringtones|Hip-Hop/Rap|Underground Rap',
4567             8172 => 'Tones|Ringtones|Hip-Hop/Rap|West Coast Rap',
4568             8173 => 'Tones|Ringtones|Holiday|Chanukah',
4569             8174 => 'Tones|Ringtones|Holiday|Christmas',
4570             8175 => "Tones|Ringtones|Holiday|Christmas: Children's",
4571             8176 => 'Tones|Ringtones|Holiday|Christmas: Classic',
4572             8177 => 'Tones|Ringtones|Holiday|Christmas: Classical',
4573             8178 => 'Tones|Ringtones|Holiday|Christmas: Jazz',
4574             8179 => 'Tones|Ringtones|Holiday|Christmas: Modern',
4575             8180 => 'Tones|Ringtones|Holiday|Christmas: Pop',
4576             8181 => 'Tones|Ringtones|Holiday|Christmas: R&B',
4577             8182 => 'Tones|Ringtones|Holiday|Christmas: Religious',
4578             8183 => 'Tones|Ringtones|Holiday|Christmas: Rock',
4579             8184 => 'Tones|Ringtones|Holiday|Easter',
4580             8185 => 'Tones|Ringtones|Holiday|Halloween',
4581             8186 => 'Tones|Ringtones|Holiday|Thanksgiving',
4582             8187 => 'Tones|Ringtones|Indian',
4583             8188 => 'Tones|Ringtones|Indian|Bollywood',
4584             8189 => 'Tones|Ringtones|Indian|Devotional & Spiritual',
4585             8190 => 'Tones|Ringtones|Indian|Ghazals',
4586             8191 => 'Tones|Ringtones|Indian|Indian Classical',
4587             8192 => 'Tones|Ringtones|Indian|Indian Folk',
4588             8193 => 'Tones|Ringtones|Indian|Indian Pop',
4589             8194 => 'Tones|Ringtones|Indian|Regional Indian',
4590             8195 => 'Tones|Ringtones|Indian|Sufi',
4591             8196 => 'Tones|Ringtones|Indian|Regional Indian|Tamil',
4592             8197 => 'Tones|Ringtones|Indian|Regional Indian|Telugu',
4593             8198 => 'Tones|Ringtones|Instrumental',
4594             8199 => 'Tones|Ringtones|Jazz|Avant-Garde Jazz',
4595             8201 => 'Tones|Ringtones|Jazz|Big Band',
4596             8202 => 'Tones|Ringtones|Jazz|Bop',
4597             8203 => 'Tones|Ringtones|Jazz|Contemporary Jazz',
4598             8204 => 'Tones|Ringtones|Jazz|Cool Jazz',
4599             8205 => 'Tones|Ringtones|Jazz|Crossover Jazz',
4600             8206 => 'Tones|Ringtones|Jazz|Dixieland',
4601             8207 => 'Tones|Ringtones|Jazz|Fusion',
4602             8208 => 'Tones|Ringtones|Jazz|Hard Bop',
4603             8209 => 'Tones|Ringtones|Jazz|Latin Jazz',
4604             8210 => 'Tones|Ringtones|Jazz|Mainstream Jazz',
4605             8211 => 'Tones|Ringtones|Jazz|Ragtime',
4606             8212 => 'Tones|Ringtones|Jazz|Smooth Jazz',
4607             8213 => 'Tones|Ringtones|Jazz|Trad Jazz',
4608             8214 => 'Tones|Ringtones|Pop|K-Pop',
4609             8215 => 'Tones|Ringtones|Karaoke',
4610             8216 => 'Tones|Ringtones|Korean',
4611             8217 => 'Tones|Ringtones|Korean|Korean Classical',
4612             8218 => 'Tones|Ringtones|Korean|Korean Trad Instrumental',
4613             8219 => 'Tones|Ringtones|Korean|Korean Trad Song',
4614             8220 => 'Tones|Ringtones|Korean|Korean Trad Theater',
4615             8221 => 'Tones|Ringtones|Latin|Alternative & Rock in Spanish',
4616             8222 => 'Tones|Ringtones|Latin|Baladas y Boleros',
4617             8223 => 'Tones|Ringtones|Latin|Contemporary Latin',
4618             8224 => 'Tones|Ringtones|Latin|Latin Jazz',
4619             8225 => 'Tones|Ringtones|Latin|Latin Urban',
4620             8226 => 'Tones|Ringtones|Latin|Pop in Spanish',
4621             8227 => 'Tones|Ringtones|Latin|Raices',
4622             8228 => 'Tones|Ringtones|Latin|Musica Mexicana', # (Música Mexicana)
4623             8229 => 'Tones|Ringtones|Latin|Salsa y Tropical',
4624             8230 => 'Tones|Ringtones|Marching Bands',
4625             8231 => 'Tones|Ringtones|New Age|Healing',
4626             8232 => 'Tones|Ringtones|New Age|Meditation',
4627             8233 => 'Tones|Ringtones|New Age|Nature',
4628             8234 => 'Tones|Ringtones|New Age|Relaxation',
4629             8235 => 'Tones|Ringtones|New Age|Travel',
4630             8236 => 'Tones|Ringtones|Orchestral',
4631             8237 => 'Tones|Ringtones|Pop|Adult Contemporary',
4632             8238 => 'Tones|Ringtones|Pop|Britpop',
4633             8239 => 'Tones|Ringtones|Pop|C-Pop',
4634             8240 => 'Tones|Ringtones|Pop|Cantopop/HK-Pop',
4635             8241 => 'Tones|Ringtones|Pop|Indo Pop',
4636             8242 => 'Tones|Ringtones|Pop|Korean Folk-Pop',
4637             8243 => 'Tones|Ringtones|Pop|Malaysian Pop',
4638             8244 => 'Tones|Ringtones|Pop|Mandopop',
4639             8245 => 'Tones|Ringtones|Pop|Manilla Sound',
4640             8246 => 'Tones|Ringtones|Pop|Oldies',
4641             8247 => 'Tones|Ringtones|Pop|Original Pilipino Music',
4642             8248 => 'Tones|Ringtones|Pop|Pinoy Pop',
4643             8249 => 'Tones|Ringtones|Pop|Pop/Rock',
4644             8250 => 'Tones|Ringtones|Pop|Soft Rock',
4645             8251 => 'Tones|Ringtones|Pop|Tai-Pop',
4646             8252 => 'Tones|Ringtones|Pop|Teen Pop',
4647             8253 => 'Tones|Ringtones|Pop|Thai Pop',
4648             8254 => 'Tones|Ringtones|R&B/Soul|Contemporary R&B',
4649             8255 => 'Tones|Ringtones|R&B/Soul|Disco',
4650             8256 => 'Tones|Ringtones|R&B/Soul|Doo Wop',
4651             8257 => 'Tones|Ringtones|R&B/Soul|Funk',
4652             8258 => 'Tones|Ringtones|R&B/Soul|Motown',
4653             8259 => 'Tones|Ringtones|R&B/Soul|Neo-Soul',
4654             8260 => 'Tones|Ringtones|R&B/Soul|Soul',
4655             8261 => 'Tones|Ringtones|Reggae|Modern Dancehall',
4656             8262 => 'Tones|Ringtones|Reggae|Dub',
4657             8263 => 'Tones|Ringtones|Reggae|Roots Reggae',
4658             8264 => 'Tones|Ringtones|Reggae|Ska',
4659             8265 => 'Tones|Ringtones|Rock|Adult Alternative',
4660             8266 => 'Tones|Ringtones|Rock|American Trad Rock',
4661             8267 => 'Tones|Ringtones|Rock|Arena Rock',
4662             8268 => 'Tones|Ringtones|Rock|Blues-Rock',
4663             8269 => 'Tones|Ringtones|Rock|British Invasion',
4664             8270 => 'Tones|Ringtones|Rock|Chinese Rock',
4665             8271 => 'Tones|Ringtones|Rock|Death Metal/Black Metal',
4666             8272 => 'Tones|Ringtones|Rock|Glam Rock',
4667             8273 => 'Tones|Ringtones|Rock|Hair Metal',
4668             8274 => 'Tones|Ringtones|Rock|Hard Rock',
4669             8275 => 'Tones|Ringtones|Rock|Metal',
4670             8276 => 'Tones|Ringtones|Rock|Jam Bands',
4671             8277 => 'Tones|Ringtones|Rock|Korean Rock',
4672             8278 => 'Tones|Ringtones|Rock|Prog-Rock/Art Rock',
4673             8279 => 'Tones|Ringtones|Rock|Psychedelic',
4674             8280 => 'Tones|Ringtones|Rock|Rock & Roll',
4675             8281 => 'Tones|Ringtones|Rock|Rockabilly',
4676             8282 => 'Tones|Ringtones|Rock|Roots Rock',
4677             8283 => 'Tones|Ringtones|Rock|Singer/Songwriter',
4678             8284 => 'Tones|Ringtones|Rock|Southern Rock',
4679             8285 => 'Tones|Ringtones|Rock|Surf',
4680             8286 => 'Tones|Ringtones|Rock|Tex-Mex',
4681             8287 => 'Tones|Ringtones|Singer/Songwriter|Alternative Folk',
4682             8288 => 'Tones|Ringtones|Singer/Songwriter|Contemporary Folk',
4683             8289 => 'Tones|Ringtones|Singer/Songwriter|Contemporary Singer/Songwriter',
4684             8290 => 'Tones|Ringtones|Singer/Songwriter|Folk-Rock',
4685             8291 => 'Tones|Ringtones|Singer/Songwriter|New Acoustic',
4686             8292 => 'Tones|Ringtones|Singer/Songwriter|Traditional Folk',
4687             8293 => 'Tones|Ringtones|Soundtrack|Foreign Cinema',
4688             8294 => 'Tones|Ringtones|Soundtrack|Musicals',
4689             8295 => 'Tones|Ringtones|Soundtrack|Original Score',
4690             8296 => 'Tones|Ringtones|Soundtrack|Sound Effects',
4691             8297 => 'Tones|Ringtones|Soundtrack|Soundtrack',
4692             8298 => 'Tones|Ringtones|Soundtrack|TV Soundtrack',
4693             8299 => 'Tones|Ringtones|Vocal|Standards',
4694             8300 => 'Tones|Ringtones|Vocal|Traditional Pop',
4695             8301 => 'Tones|Ringtones|Vocal|Trot',
4696             8302 => 'Tones|Ringtones|Jazz|Vocal Jazz',
4697             8303 => 'Tones|Ringtones|Vocal|Vocal Pop',
4698             8304 => 'Tones|Ringtones|African',
4699             8305 => 'Tones|Ringtones|African|Afrikaans',
4700             8306 => 'Tones|Ringtones|African|Afro-Beat',
4701             8307 => 'Tones|Ringtones|African|Afro-Pop',
4702             8308 => 'Tones|Ringtones|Turkish|Arabesque',
4703             8309 => 'Tones|Ringtones|World|Asia',
4704             8310 => 'Tones|Ringtones|World|Australia',
4705             8311 => 'Tones|Ringtones|World|Cajun',
4706             8312 => 'Tones|Ringtones|World|Calypso',
4707             8313 => 'Tones|Ringtones|World|Caribbean',
4708             8314 => 'Tones|Ringtones|World|Celtic',
4709             8315 => 'Tones|Ringtones|World|Celtic Folk',
4710             8316 => 'Tones|Ringtones|World|Contemporary Celtic',
4711             8317 => 'Tones|Ringtones|World|Dangdut',
4712             8318 => 'Tones|Ringtones|World|Dini',
4713             8319 => 'Tones|Ringtones|World|Europe',
4714             8320 => 'Tones|Ringtones|World|Fado',
4715             8321 => 'Tones|Ringtones|World|Farsi',
4716             8322 => 'Tones|Ringtones|World|Flamenco',
4717             8323 => 'Tones|Ringtones|World|France',
4718             8324 => 'Tones|Ringtones|Turkish|Halk',
4719             8325 => 'Tones|Ringtones|World|Hawaii',
4720             8326 => 'Tones|Ringtones|World|Iberia',
4721             8327 => 'Tones|Ringtones|World|Indonesian Religious',
4722             8328 => 'Tones|Ringtones|World|Israeli',
4723             8329 => 'Tones|Ringtones|World|Japan',
4724             8330 => 'Tones|Ringtones|World|Klezmer',
4725             8331 => 'Tones|Ringtones|World|North America',
4726             8332 => 'Tones|Ringtones|World|Polka',
4727             8333 => 'Tones|Ringtones|Russian',
4728             8334 => 'Tones|Ringtones|Russian|Russian Chanson',
4729             8335 => 'Tones|Ringtones|Turkish|Sanat',
4730             8336 => 'Tones|Ringtones|World|Soca',
4731             8337 => 'Tones|Ringtones|World|South Africa',
4732             8338 => 'Tones|Ringtones|World|South America',
4733             8339 => 'Tones|Ringtones|World|Tango',
4734             8340 => 'Tones|Ringtones|World|Traditional Celtic',
4735             8341 => 'Tones|Ringtones|Turkish',
4736             8342 => 'Tones|Ringtones|World|Worldbeat',
4737             8343 => 'Tones|Ringtones|World|Zydeco',
4738             8345 => 'Tones|Ringtones|Classical|Art Song',
4739             8346 => 'Tones|Ringtones|Classical|Brass & Woodwinds',
4740             8347 => 'Tones|Ringtones|Classical|Solo Instrumental',
4741             8348 => 'Tones|Ringtones|Classical|Contemporary Era',
4742             8349 => 'Tones|Ringtones|Classical|Oratorio',
4743             8350 => 'Tones|Ringtones|Classical|Cantata',
4744             8351 => 'Tones|Ringtones|Classical|Electronic',
4745             8352 => 'Tones|Ringtones|Classical|Sacred',
4746             8353 => 'Tones|Ringtones|Classical|Guitar',
4747             8354 => 'Tones|Ringtones|Classical|Piano',
4748             8355 => 'Tones|Ringtones|Classical|Violin',
4749             8356 => 'Tones|Ringtones|Classical|Cello',
4750             8357 => 'Tones|Ringtones|Classical|Percussion',
4751             8358 => 'Tones|Ringtones|Electronic|Dubstep',
4752             8359 => 'Tones|Ringtones|Electronic|Bass',
4753             8360 => 'Tones|Ringtones|Hip-Hop/Rap|UK Hip Hop',
4754             8361 => 'Tones|Ringtones|Reggae|Lovers Rock',
4755             8362 => 'Tones|Ringtones|Alternative|EMO',
4756             8363 => 'Tones|Ringtones|Alternative|Pop Punk',
4757             8364 => 'Tones|Ringtones|Alternative|Indie Pop',
4758             8365 => 'Tones|Ringtones|New Age|Yoga',
4759             8366 => 'Tones|Ringtones|Pop|Tribute',
4760             8367 => 'Tones|Ringtones|Pop|Shows',
4761             8368 => 'Tones|Ringtones|Cuban',
4762             8369 => 'Tones|Ringtones|Cuban|Mambo',
4763             8370 => 'Tones|Ringtones|Cuban|Chachacha',
4764             8371 => 'Tones|Ringtones|Cuban|Guajira',
4765             8372 => 'Tones|Ringtones|Cuban|Son',
4766             8373 => 'Tones|Ringtones|Cuban|Bolero',
4767             8374 => 'Tones|Ringtones|Cuban|Guaracha',
4768             8375 => 'Tones|Ringtones|Cuban|Timba',
4769             8376 => 'Tones|Ringtones|Soundtrack|Video Game',
4770             8377 => 'Tones|Ringtones|Indian|Regional Indian|Punjabi|Punjabi Pop',
4771             8378 => 'Tones|Ringtones|Indian|Regional Indian|Bengali|Rabindra Sangeet',
4772             8379 => 'Tones|Ringtones|Indian|Regional Indian|Malayalam',
4773             8380 => 'Tones|Ringtones|Indian|Regional Indian|Kannada',
4774             8381 => 'Tones|Ringtones|Indian|Regional Indian|Marathi',
4775             8382 => 'Tones|Ringtones|Indian|Regional Indian|Gujarati',
4776             8383 => 'Tones|Ringtones|Indian|Regional Indian|Assamese',
4777             8384 => 'Tones|Ringtones|Indian|Regional Indian|Bhojpuri',
4778             8385 => 'Tones|Ringtones|Indian|Regional Indian|Haryanvi',
4779             8386 => 'Tones|Ringtones|Indian|Regional Indian|Odia',
4780             8387 => 'Tones|Ringtones|Indian|Regional Indian|Rajasthani',
4781             8388 => 'Tones|Ringtones|Indian|Regional Indian|Urdu',
4782             8389 => 'Tones|Ringtones|Indian|Regional Indian|Punjabi',
4783             8390 => 'Tones|Ringtones|Indian|Regional Indian|Bengali',
4784             8391 => 'Tones|Ringtones|Indian|Indian Classical|Carnatic Classical',
4785             8392 => 'Tones|Ringtones|Indian|Indian Classical|Hindustani Classical',
4786             8393 => 'Tones|Ringtones|African|Afro House',
4787             8394 => 'Tones|Ringtones|African|Afro Soul',
4788             8395 => 'Tones|Ringtones|African|Afrobeats',
4789             8396 => 'Tones|Ringtones|African|Benga',
4790             8397 => 'Tones|Ringtones|African|Bongo-Flava',
4791             8398 => 'Tones|Ringtones|African|Coupe-Decale',
4792             8399 => 'Tones|Ringtones|African|Gqom',
4793             8400 => 'Tones|Ringtones|African|Highlife',
4794             8401 => 'Tones|Ringtones|African|Kuduro',
4795             8402 => 'Tones|Ringtones|African|Kizomba',
4796             8403 => 'Tones|Ringtones|African|Kwaito',
4797             8404 => 'Tones|Ringtones|African|Mbalax',
4798             8405 => 'Tones|Ringtones|African|Ndombolo',
4799             8406 => 'Tones|Ringtones|African|Shangaan Electro',
4800             8407 => 'Tones|Ringtones|African|Soukous',
4801             8408 => 'Tones|Ringtones|African|Taarab',
4802             8409 => 'Tones|Ringtones|African|Zouglou',
4803             8410 => 'Tones|Ringtones|Turkish|Ozgun',
4804             8411 => 'Tones|Ringtones|Turkish|Fantezi',
4805             8412 => 'Tones|Ringtones|Turkish|Religious',
4806             8413 => 'Tones|Ringtones|Pop|Turkish Pop',
4807             8414 => 'Tones|Ringtones|Rock|Turkish Rock',
4808             8415 => 'Tones|Ringtones|Alternative|Turkish Alternative',
4809             8416 => 'Tones|Ringtones|Hip-Hop/Rap|Turkish Hip-Hop/Rap',
4810             8417 => 'Tones|Ringtones|African|Maskandi',
4811             8418 => 'Tones|Ringtones|Russian|Russian Romance',
4812             8419 => 'Tones|Ringtones|Russian|Russian Bard',
4813             8420 => 'Tones|Ringtones|Russian|Russian Pop',
4814             8421 => 'Tones|Ringtones|Russian|Russian Rock',
4815             8422 => 'Tones|Ringtones|Russian|Russian Hip-Hop',
4816             8423 => 'Tones|Ringtones|Arabic|Levant',
4817             8424 => 'Tones|Ringtones|Arabic|Levant|Dabke',
4818             8425 => 'Tones|Ringtones|Arabic|Maghreb Rai',
4819             8426 => 'Tones|Ringtones|Arabic|Khaleeji|Khaleeji Jalsat',
4820             8427 => 'Tones|Ringtones|Arabic|Khaleeji|Khaleeji Shailat',
4821             8428 => 'Tones|Ringtones|Tarab',
4822             8429 => 'Tones|Ringtones|Tarab|Iraqi Tarab',
4823             8430 => 'Tones|Ringtones|Tarab|Egyptian Tarab',
4824             8431 => 'Tones|Ringtones|Tarab|Khaleeji Tarab',
4825             8432 => 'Tones|Ringtones|Pop|Levant Pop',
4826             8433 => 'Tones|Ringtones|Pop|Iraqi Pop',
4827             8434 => 'Tones|Ringtones|Pop|Egyptian Pop',
4828             8435 => 'Tones|Ringtones|Pop|Maghreb Pop',
4829             8436 => 'Tones|Ringtones|Pop|Khaleeji Pop',
4830             8437 => 'Tones|Ringtones|Hip-Hop/Rap|Levant Hip-Hop',
4831             8438 => 'Tones|Ringtones|Hip-Hop/Rap|Egyptian Hip-Hop',
4832             8439 => 'Tones|Ringtones|Hip-Hop/Rap|Maghreb Hip-Hop',
4833             8440 => 'Tones|Ringtones|Hip-Hop/Rap|Khaleeji Hip-Hop',
4834             8441 => 'Tones|Ringtones|Alternative|Indie Levant',
4835             8442 => 'Tones|Ringtones|Alternative|Indie Egyptian',
4836             8443 => 'Tones|Ringtones|Alternative|Indie Maghreb',
4837             8444 => 'Tones|Ringtones|Electronic|Levant Electronic',
4838             8445 => "Tones|Ringtones|Electronic|Electro-Cha'abi",
4839             8446 => 'Tones|Ringtones|Electronic|Maghreb Electronic',
4840             8447 => 'Tones|Ringtones|Folk|Iraqi Folk',
4841             8448 => 'Tones|Ringtones|Folk|Khaleeji Folk',
4842             8449 => 'Tones|Ringtones|Dance|Maghreb Dance',
4843             9002 => 'Books|Nonfiction',
4844             9003 => 'Books|Romance',
4845             9004 => 'Books|Travel & Adventure',
4846             9007 => 'Books|Arts & Entertainment',
4847             9008 => 'Books|Biographies & Memoirs',
4848             9009 => 'Books|Business & Personal Finance',
4849             9010 => 'Books|Children & Teens',
4850             9012 => 'Books|Humor',
4851             9015 => 'Books|History',
4852             9018 => 'Books|Religion & Spirituality',
4853             9019 => 'Books|Science & Nature',
4854             9020 => 'Books|Sci-Fi & Fantasy',
4855             9024 => 'Books|Lifestyle & Home',
4856             9025 => 'Books|Self-Development',
4857             9026 => 'Books|Comics & Graphic Novels',
4858             9027 => 'Books|Computers & Internet',
4859             9028 => 'Books|Cookbooks, Food & Wine',
4860             9029 => 'Books|Professional & Technical',
4861             9030 => 'Books|Parenting',
4862             9031 => 'Books|Fiction & Literature',
4863             9032 => 'Books|Mysteries & Thrillers',
4864             9033 => 'Books|Reference',
4865             9034 => 'Books|Politics & Current Events',
4866             9035 => 'Books|Sports & Outdoors',
4867             10001 => 'Books|Lifestyle & Home|Antiques & Collectibles',
4868             10002 => 'Books|Arts & Entertainment|Art & Architecture',
4869             10003 => 'Books|Religion & Spirituality|Bibles',
4870             10004 => 'Books|Self-Development|Spirituality',
4871             10005 => 'Books|Business & Personal Finance|Industries & Professions',
4872             10006 => 'Books|Business & Personal Finance|Marketing & Sales',
4873             10007 => 'Books|Business & Personal Finance|Small Business & Entrepreneurship',
4874             10008 => 'Books|Business & Personal Finance|Personal Finance',
4875             10009 => 'Books|Business & Personal Finance|Reference',
4876             10010 => 'Books|Business & Personal Finance|Careers',
4877             10011 => 'Books|Business & Personal Finance|Economics',
4878             10012 => 'Books|Business & Personal Finance|Investing',
4879             10013 => 'Books|Business & Personal Finance|Finance',
4880             10014 => 'Books|Business & Personal Finance|Management & Leadership',
4881             10015 => 'Books|Comics & Graphic Novels|Graphic Novels',
4882             10016 => 'Books|Comics & Graphic Novels|Manga',
4883             10017 => 'Books|Computers & Internet|Computers',
4884             10018 => 'Books|Computers & Internet|Databases',
4885             10019 => 'Books|Computers & Internet|Digital Media',
4886             10020 => 'Books|Computers & Internet|Internet',
4887             10021 => 'Books|Computers & Internet|Network',
4888             10022 => 'Books|Computers & Internet|Operating Systems',
4889             10023 => 'Books|Computers & Internet|Programming',
4890             10024 => 'Books|Computers & Internet|Software',
4891             10025 => 'Books|Computers & Internet|System Administration',
4892             10026 => 'Books|Cookbooks, Food & Wine|Beverages',
4893             10027 => 'Books|Cookbooks, Food & Wine|Courses & Dishes',
4894             10028 => 'Books|Cookbooks, Food & Wine|Special Diet',
4895             10029 => 'Books|Cookbooks, Food & Wine|Special Occasions',
4896             10030 => 'Books|Cookbooks, Food & Wine|Methods',
4897             10031 => 'Books|Cookbooks, Food & Wine|Reference',
4898             10032 => 'Books|Cookbooks, Food & Wine|Regional & Ethnic',
4899             10033 => 'Books|Cookbooks, Food & Wine|Specific Ingredients',
4900             10034 => 'Books|Lifestyle & Home|Crafts & Hobbies',
4901             10035 => 'Books|Professional & Technical|Design',
4902             10036 => 'Books|Arts & Entertainment|Theater',
4903             10037 => 'Books|Professional & Technical|Education',
4904             10038 => 'Books|Nonfiction|Family & Relationships',
4905             10039 => 'Books|Fiction & Literature|Action & Adventure',
4906             10040 => 'Books|Fiction & Literature|African American',
4907             10041 => 'Books|Fiction & Literature|Religious',
4908             10042 => 'Books|Fiction & Literature|Classics',
4909             10043 => 'Books|Fiction & Literature|Erotica',
4910             10044 => 'Books|Sci-Fi & Fantasy|Fantasy',
4911             10045 => 'Books|Fiction & Literature|Gay',
4912             10046 => 'Books|Fiction & Literature|Ghost',
4913             10047 => 'Books|Fiction & Literature|Historical',
4914             10048 => 'Books|Fiction & Literature|Horror',
4915             10049 => 'Books|Fiction & Literature|Literary',
4916             10050 => 'Books|Mysteries & Thrillers|Hard-Boiled',
4917             10051 => 'Books|Mysteries & Thrillers|Historical',
4918             10052 => 'Books|Mysteries & Thrillers|Police Procedural',
4919             10053 => 'Books|Mysteries & Thrillers|Short Stories',
4920             10054 => 'Books|Mysteries & Thrillers|British Detectives',
4921             10055 => 'Books|Mysteries & Thrillers|Women Sleuths',
4922             10056 => 'Books|Romance|Erotic Romance',
4923             10057 => 'Books|Romance|Contemporary',
4924             10058 => 'Books|Romance|Paranormal',
4925             10059 => 'Books|Romance|Historical',
4926             10060 => 'Books|Romance|Short Stories',
4927             10061 => 'Books|Romance|Suspense',
4928             10062 => 'Books|Romance|Western',
4929             10063 => 'Books|Sci-Fi & Fantasy|Science Fiction',
4930             10064 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature',
4931             10065 => 'Books|Fiction & Literature|Short Stories',
4932             10066 => 'Books|Reference|Foreign Languages',
4933             10067 => 'Books|Arts & Entertainment|Games',
4934             10068 => 'Books|Lifestyle & Home|Gardening',
4935             10069 => 'Books|Self-Development|Health & Fitness',
4936             10070 => 'Books|History|Africa',
4937             10071 => 'Books|History|Americas',
4938             10072 => 'Books|History|Ancient',
4939             10073 => 'Books|History|Asia',
4940             10074 => 'Books|History|Australia & Oceania',
4941             10075 => 'Books|History|Europe',
4942             10076 => 'Books|History|Latin America',
4943             10077 => 'Books|History|Middle East',
4944             10078 => 'Books|History|Military',
4945             10079 => 'Books|History|United States',
4946             10080 => 'Books|History|World',
4947             10081 => "Books|Children & Teens|Children's Fiction",
4948             10082 => "Books|Children & Teens|Children's Nonfiction",
4949             10083 => 'Books|Professional & Technical|Law',
4950             10084 => 'Books|Fiction & Literature|Literary Criticism',
4951             10085 => 'Books|Science & Nature|Mathematics',
4952             10086 => 'Books|Professional & Technical|Medical',
4953             10087 => 'Books|Arts & Entertainment|Music',
4954             10088 => 'Books|Science & Nature|Nature',
4955             10089 => 'Books|Arts & Entertainment|Performing Arts',
4956             10090 => 'Books|Lifestyle & Home|Pets',
4957             10091 => 'Books|Nonfiction|Philosophy',
4958             10092 => 'Books|Arts & Entertainment|Photography',
4959             10093 => 'Books|Fiction & Literature|Poetry',
4960             10094 => 'Books|Self-Development|Psychology',
4961             10095 => 'Books|Reference|Almanacs & Yearbooks',
4962             10096 => 'Books|Reference|Atlases & Maps',
4963             10097 => 'Books|Reference|Catalogs & Directories',
4964             10098 => 'Books|Reference|Consumer Guides',
4965             10099 => 'Books|Reference|Dictionaries & Thesauruses',
4966             10100 => 'Books|Reference|Encyclopedias',
4967             10101 => 'Books|Reference|Etiquette',
4968             10102 => 'Books|Reference|Quotations',
4969             10103 => 'Books|Reference|Words & Language',
4970             10104 => 'Books|Reference|Writing',
4971             10105 => 'Books|Religion & Spirituality|Bible Studies',
4972             10106 => 'Books|Religion & Spirituality|Buddhism',
4973             10107 => 'Books|Religion & Spirituality|Christianity',
4974             10108 => 'Books|Religion & Spirituality|Hinduism',
4975             10109 => 'Books|Religion & Spirituality|Islam',
4976             10110 => 'Books|Religion & Spirituality|Judaism',
4977             10111 => 'Books|Science & Nature|Astronomy',
4978             10112 => 'Books|Science & Nature|Chemistry',
4979             10113 => 'Books|Science & Nature|Earth Sciences',
4980             10114 => 'Books|Science & Nature|Essays',
4981             10115 => 'Books|Science & Nature|History',
4982             10116 => 'Books|Science & Nature|Life Sciences',
4983             10117 => 'Books|Science & Nature|Physics',
4984             10118 => 'Books|Science & Nature|Reference',
4985             10119 => 'Books|Self-Development|Self-Improvement',
4986             10120 => 'Books|Nonfiction|Social Science',
4987             10121 => 'Books|Sports & Outdoors|Baseball',
4988             10122 => 'Books|Sports & Outdoors|Basketball',
4989             10123 => 'Books|Sports & Outdoors|Coaching',
4990             10124 => 'Books|Sports & Outdoors|Extreme Sports',
4991             10125 => 'Books|Sports & Outdoors|Football',
4992             10126 => 'Books|Sports & Outdoors|Golf',
4993             10127 => 'Books|Sports & Outdoors|Hockey',
4994             10128 => 'Books|Sports & Outdoors|Mountaineering',
4995             10129 => 'Books|Sports & Outdoors|Outdoors',
4996             10130 => 'Books|Sports & Outdoors|Racket Sports',
4997             10131 => 'Books|Sports & Outdoors|Reference',
4998             10132 => 'Books|Sports & Outdoors|Soccer',
4999             10133 => 'Books|Sports & Outdoors|Training',
5000             10134 => 'Books|Sports & Outdoors|Water Sports',
5001             10135 => 'Books|Sports & Outdoors|Winter Sports',
5002             10136 => 'Books|Reference|Study Aids',
5003             10137 => 'Books|Professional & Technical|Engineering',
5004             10138 => 'Books|Nonfiction|Transportation',
5005             10139 => 'Books|Travel & Adventure|Africa',
5006             10140 => 'Books|Travel & Adventure|Asia',
5007             10141 => 'Books|Travel & Adventure|Specialty Travel',
5008             10142 => 'Books|Travel & Adventure|Canada',
5009             10143 => 'Books|Travel & Adventure|Caribbean',
5010             10144 => 'Books|Travel & Adventure|Latin America',
5011             10145 => 'Books|Travel & Adventure|Essays & Memoirs',
5012             10146 => 'Books|Travel & Adventure|Europe',
5013             10147 => 'Books|Travel & Adventure|Middle East',
5014             10148 => 'Books|Travel & Adventure|United States',
5015             10149 => 'Books|Nonfiction|True Crime',
5016             11001 => 'Books|Sci-Fi & Fantasy|Fantasy|Contemporary',
5017             11002 => 'Books|Sci-Fi & Fantasy|Fantasy|Epic',
5018             11003 => 'Books|Sci-Fi & Fantasy|Fantasy|Historical',
5019             11004 => 'Books|Sci-Fi & Fantasy|Fantasy|Paranormal',
5020             11005 => 'Books|Sci-Fi & Fantasy|Fantasy|Short Stories',
5021             11006 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature|Adventure',
5022             11007 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature|High Tech',
5023             11008 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature|Short Stories',
5024             11009 => 'Books|Professional & Technical|Education|Language Arts & Disciplines',
5025             11010 => 'Books|Communications & Media',
5026             11011 => 'Books|Communications & Media|Broadcasting',
5027             11012 => 'Books|Communications & Media|Digital Media',
5028             11013 => 'Books|Communications & Media|Journalism',
5029             11014 => 'Books|Communications & Media|Photojournalism',
5030             11015 => 'Books|Communications & Media|Print',
5031             11016 => 'Books|Communications & Media|Speech',
5032             11017 => 'Books|Communications & Media|Writing',
5033             11018 => 'Books|Arts & Entertainment|Art & Architecture|Urban Planning',
5034             11019 => 'Books|Arts & Entertainment|Dance',
5035             11020 => 'Books|Arts & Entertainment|Fashion',
5036             11021 => 'Books|Arts & Entertainment|Film',
5037             11022 => 'Books|Arts & Entertainment|Interior Design',
5038             11023 => 'Books|Arts & Entertainment|Media Arts',
5039             11024 => 'Books|Arts & Entertainment|Radio',
5040             11025 => 'Books|Arts & Entertainment|TV',
5041             11026 => 'Books|Arts & Entertainment|Visual Arts',
5042             11027 => 'Books|Biographies & Memoirs|Arts & Entertainment',
5043             11028 => 'Books|Biographies & Memoirs|Business',
5044             11029 => 'Books|Biographies & Memoirs|Culinary',
5045             11030 => 'Books|Biographies & Memoirs|Gay & Lesbian',
5046             11031 => 'Books|Biographies & Memoirs|Historical',
5047             11032 => 'Books|Biographies & Memoirs|Literary',
5048             11033 => 'Books|Biographies & Memoirs|Media & Journalism',
5049             11034 => 'Books|Biographies & Memoirs|Military',
5050             11035 => 'Books|Biographies & Memoirs|Politics',
5051             11036 => 'Books|Biographies & Memoirs|Religious',
5052             11037 => 'Books|Biographies & Memoirs|Science & Technology',
5053             11038 => 'Books|Biographies & Memoirs|Sports',
5054             11039 => 'Books|Biographies & Memoirs|Women',
5055             11040 => 'Books|Romance|New Adult',
5056             11042 => 'Books|Romance|Romantic Comedy',
5057             11043 => 'Books|Romance|Gay & Lesbian',
5058             11044 => 'Books|Fiction & Literature|Essays',
5059             11045 => 'Books|Fiction & Literature|Anthologies',
5060             11046 => 'Books|Fiction & Literature|Comparative Literature',
5061             11047 => 'Books|Fiction & Literature|Drama',
5062             11049 => 'Books|Fiction & Literature|Fairy Tales, Myths & Fables',
5063             11050 => 'Books|Fiction & Literature|Family',
5064             11051 => 'Books|Comics & Graphic Novels|Manga|School Drama',
5065             11052 => 'Books|Comics & Graphic Novels|Manga|Human Drama',
5066             11053 => 'Books|Comics & Graphic Novels|Manga|Family Drama',
5067             11054 => 'Books|Sports & Outdoors|Boxing',
5068             11055 => 'Books|Sports & Outdoors|Cricket',
5069             11056 => 'Books|Sports & Outdoors|Cycling',
5070             11057 => 'Books|Sports & Outdoors|Equestrian',
5071             11058 => 'Books|Sports & Outdoors|Martial Arts & Self Defense',
5072             11059 => 'Books|Sports & Outdoors|Motor Sports',
5073             11060 => 'Books|Sports & Outdoors|Rugby',
5074             11061 => 'Books|Sports & Outdoors|Running',
5075             11062 => 'Books|Self-Development|Diet & Nutrition',
5076             11063 => 'Books|Science & Nature|Agriculture',
5077             11064 => 'Books|Science & Nature|Atmosphere',
5078             11065 => 'Books|Science & Nature|Biology',
5079             11066 => 'Books|Science & Nature|Ecology',
5080             11067 => 'Books|Science & Nature|Environment',
5081             11068 => 'Books|Science & Nature|Geography',
5082             11069 => 'Books|Science & Nature|Geology',
5083             11070 => 'Books|Nonfiction|Social Science|Anthropology',
5084             11071 => 'Books|Nonfiction|Social Science|Archaeology',
5085             11072 => 'Books|Nonfiction|Social Science|Civics',
5086             11073 => 'Books|Nonfiction|Social Science|Government',
5087             11074 => 'Books|Nonfiction|Social Science|Social Studies',
5088             11075 => 'Books|Nonfiction|Social Science|Social Welfare',
5089             11076 => 'Books|Nonfiction|Social Science|Society',
5090             11077 => 'Books|Nonfiction|Philosophy|Aesthetics',
5091             11078 => 'Books|Nonfiction|Philosophy|Epistemology',
5092             11079 => 'Books|Nonfiction|Philosophy|Ethics',
5093             11080 => 'Books|Nonfiction|Philosophy|Language',
5094             11081 => 'Books|Nonfiction|Philosophy|Logic',
5095             11082 => 'Books|Nonfiction|Philosophy|Metaphysics',
5096             11083 => 'Books|Nonfiction|Philosophy|Political',
5097             11084 => 'Books|Nonfiction|Philosophy|Religion',
5098             11085 => 'Books|Reference|Manuals',
5099             11086 => 'Books|Kids',
5100             11087 => 'Books|Kids|Animals',
5101             11088 => 'Books|Kids|Basic Concepts',
5102             11089 => 'Books|Kids|Basic Concepts|Alphabet',
5103             11090 => 'Books|Kids|Basic Concepts|Body',
5104             11091 => 'Books|Kids|Basic Concepts|Colors',
5105             11092 => 'Books|Kids|Basic Concepts|Counting & Numbers',
5106             11093 => 'Books|Kids|Basic Concepts|Date & Time',
5107             11094 => 'Books|Kids|Basic Concepts|General',
5108             11095 => 'Books|Kids|Basic Concepts|Money',
5109             11096 => 'Books|Kids|Basic Concepts|Opposites',
5110             11097 => 'Books|Kids|Basic Concepts|Seasons',
5111             11098 => 'Books|Kids|Basic Concepts|Senses & Sensation',
5112             11099 => 'Books|Kids|Basic Concepts|Size & Shape',
5113             11100 => 'Books|Kids|Basic Concepts|Sounds',
5114             11101 => 'Books|Kids|Basic Concepts|Words',
5115             11102 => 'Books|Kids|Biography',
5116             11103 => 'Books|Kids|Careers & Occupations',
5117             11104 => 'Books|Kids|Computers & Technology',
5118             11105 => 'Books|Kids|Cooking & Food',
5119             11106 => 'Books|Kids|Arts & Entertainment',
5120             11107 => 'Books|Kids|Arts & Entertainment|Art',
5121             11108 => 'Books|Kids|Arts & Entertainment|Crafts',
5122             11109 => 'Books|Kids|Arts & Entertainment|Music',
5123             11110 => 'Books|Kids|Arts & Entertainment|Performing Arts',
5124             11111 => 'Books|Kids|Family',
5125             11112 => 'Books|Kids|Fiction',
5126             11113 => 'Books|Kids|Fiction|Action & Adventure',
5127             11114 => 'Books|Kids|Fiction|Animals',
5128             11115 => 'Books|Kids|Fiction|Classics',
5129             11116 => 'Books|Kids|Fiction|Comics & Graphic Novels',
5130             11117 => 'Books|Kids|Fiction|Culture, Places & People',
5131             11118 => 'Books|Kids|Fiction|Family & Relationships',
5132             11119 => 'Books|Kids|Fiction|Fantasy',
5133             11120 => 'Books|Kids|Fiction|Fairy Tales, Myths & Fables',
5134             11121 => 'Books|Kids|Fiction|Favorite Characters',
5135             11122 => 'Books|Kids|Fiction|Historical',
5136             11123 => 'Books|Kids|Fiction|Holidays & Celebrations',
5137             11124 => 'Books|Kids|Fiction|Monsters & Ghosts',
5138             11125 => 'Books|Kids|Fiction|Mysteries',
5139             11126 => 'Books|Kids|Fiction|Nature',
5140             11127 => 'Books|Kids|Fiction|Religion',
5141             11128 => 'Books|Kids|Fiction|Sci-Fi',
5142             11129 => 'Books|Kids|Fiction|Social Issues',
5143             11130 => 'Books|Kids|Fiction|Sports & Recreation',
5144             11131 => 'Books|Kids|Fiction|Transportation',
5145             11132 => 'Books|Kids|Games & Activities',
5146             11133 => 'Books|Kids|General Nonfiction',
5147             11134 => 'Books|Kids|Health',
5148             11135 => 'Books|Kids|History',
5149             11136 => 'Books|Kids|Holidays & Celebrations',
5150             11137 => 'Books|Kids|Holidays & Celebrations|Birthdays',
5151             11138 => 'Books|Kids|Holidays & Celebrations|Christmas & Advent',
5152             11139 => 'Books|Kids|Holidays & Celebrations|Easter & Lent',
5153             11140 => 'Books|Kids|Holidays & Celebrations|General',
5154             11141 => 'Books|Kids|Holidays & Celebrations|Halloween',
5155             11142 => 'Books|Kids|Holidays & Celebrations|Hanukkah',
5156             11143 => 'Books|Kids|Holidays & Celebrations|Other',
5157             11144 => 'Books|Kids|Holidays & Celebrations|Passover',
5158             11145 => 'Books|Kids|Holidays & Celebrations|Patriotic Holidays',
5159             11146 => 'Books|Kids|Holidays & Celebrations|Ramadan',
5160             11147 => 'Books|Kids|Holidays & Celebrations|Thanksgiving',
5161             11148 => "Books|Kids|Holidays & Celebrations|Valentine's Day",
5162             11149 => 'Books|Kids|Humor',
5163             11150 => 'Books|Kids|Humor|Jokes & Riddles',
5164             11151 => 'Books|Kids|Poetry',
5165             11152 => 'Books|Kids|Learning to Read',
5166             11153 => 'Books|Kids|Learning to Read|Chapter Books',
5167             11154 => 'Books|Kids|Learning to Read|Early Readers',
5168             11155 => 'Books|Kids|Learning to Read|Intermediate Readers',
5169             11156 => 'Books|Kids|Nursery Rhymes',
5170             11157 => 'Books|Kids|Government',
5171             11158 => 'Books|Kids|Reference',
5172             11159 => 'Books|Kids|Religion',
5173             11160 => 'Books|Kids|Science & Nature',
5174             11161 => 'Books|Kids|Social Issues',
5175             11162 => 'Books|Kids|Social Studies',
5176             11163 => 'Books|Kids|Sports & Recreation',
5177             11164 => 'Books|Kids|Transportation',
5178             11165 => 'Books|Young Adult',
5179             11166 => 'Books|Young Adult|Animals',
5180             11167 => 'Books|Young Adult|Biography',
5181             11168 => 'Books|Young Adult|Careers & Occupations',
5182             11169 => 'Books|Young Adult|Computers & Technology',
5183             11170 => 'Books|Young Adult|Cooking & Food',
5184             11171 => 'Books|Young Adult|Arts & Entertainment',
5185             11172 => 'Books|Young Adult|Arts & Entertainment|Art',
5186             11173 => 'Books|Young Adult|Arts & Entertainment|Crafts',
5187             11174 => 'Books|Young Adult|Arts & Entertainment|Music',
5188             11175 => 'Books|Young Adult|Arts & Entertainment|Performing Arts',
5189             11176 => 'Books|Young Adult|Family',
5190             11177 => 'Books|Young Adult|Fiction',
5191             11178 => 'Books|Young Adult|Fiction|Action & Adventure',
5192             11179 => 'Books|Young Adult|Fiction|Animals',
5193             11180 => 'Books|Young Adult|Fiction|Classics',
5194             11181 => 'Books|Young Adult|Fiction|Comics & Graphic Novels',
5195             11182 => 'Books|Young Adult|Fiction|Culture, Places & People',
5196             11183 => 'Books|Young Adult|Fiction|Dystopian',
5197             11184 => 'Books|Young Adult|Fiction|Family & Relationships',
5198             11185 => 'Books|Young Adult|Fiction|Fantasy',
5199             11186 => 'Books|Young Adult|Fiction|Fairy Tales, Myths & Fables',
5200             11187 => 'Books|Young Adult|Fiction|Favorite Characters',
5201             11188 => 'Books|Young Adult|Fiction|Historical',
5202             11189 => 'Books|Young Adult|Fiction|Holidays & Celebrations',
5203             11190 => 'Books|Young Adult|Fiction|Horror, Monsters & Ghosts',
5204             11191 => 'Books|Young Adult|Fiction|Crime & Mystery',
5205             11192 => 'Books|Young Adult|Fiction|Nature',
5206             11193 => 'Books|Young Adult|Fiction|Religion',
5207             11194 => 'Books|Young Adult|Fiction|Romance',
5208             11195 => 'Books|Young Adult|Fiction|Sci-Fi',
5209             11196 => 'Books|Young Adult|Fiction|Coming of Age',
5210             11197 => 'Books|Young Adult|Fiction|Sports & Recreation',
5211             11198 => 'Books|Young Adult|Fiction|Transportation',
5212             11199 => 'Books|Young Adult|Games & Activities',
5213             11200 => 'Books|Young Adult|General Nonfiction',
5214             11201 => 'Books|Young Adult|Health',
5215             11202 => 'Books|Young Adult|History',
5216             11203 => 'Books|Young Adult|Holidays & Celebrations',
5217             11204 => 'Books|Young Adult|Holidays & Celebrations|Birthdays',
5218             11205 => 'Books|Young Adult|Holidays & Celebrations|Christmas & Advent',
5219             11206 => 'Books|Young Adult|Holidays & Celebrations|Easter & Lent',
5220             11207 => 'Books|Young Adult|Holidays & Celebrations|General',
5221             11208 => 'Books|Young Adult|Holidays & Celebrations|Halloween',
5222             11209 => 'Books|Young Adult|Holidays & Celebrations|Hanukkah',
5223             11210 => 'Books|Young Adult|Holidays & Celebrations|Other',
5224             11211 => 'Books|Young Adult|Holidays & Celebrations|Passover',
5225             11212 => 'Books|Young Adult|Holidays & Celebrations|Patriotic Holidays',
5226             11213 => 'Books|Young Adult|Holidays & Celebrations|Ramadan',
5227             11214 => 'Books|Young Adult|Holidays & Celebrations|Thanksgiving',
5228             11215 => "Books|Young Adult|Holidays & Celebrations|Valentine's Day",
5229             11216 => 'Books|Young Adult|Humor',
5230             11217 => 'Books|Young Adult|Humor|Jokes & Riddles',
5231             11218 => 'Books|Young Adult|Poetry',
5232             11219 => 'Books|Young Adult|Politics & Government',
5233             11220 => 'Books|Young Adult|Reference',
5234             11221 => 'Books|Young Adult|Religion',
5235             11222 => 'Books|Young Adult|Science & Nature',
5236             11223 => 'Books|Young Adult|Coming of Age',
5237             11224 => 'Books|Young Adult|Social Studies',
5238             11225 => 'Books|Young Adult|Sports & Recreation',
5239             11226 => 'Books|Young Adult|Transportation',
5240             11227 => 'Books|Communications & Media',
5241             11228 => 'Books|Military & Warfare',
5242             11229 => 'Books|Romance|Inspirational',
5243             11231 => 'Books|Romance|Holiday',
5244             11232 => 'Books|Romance|Wholesome',
5245             11233 => 'Books|Romance|Military',
5246             11234 => 'Books|Arts & Entertainment|Art History',
5247             11236 => 'Books|Arts & Entertainment|Design',
5248             11243 => 'Books|Business & Personal Finance|Accounting',
5249             11244 => 'Books|Business & Personal Finance|Hospitality',
5250             11245 => 'Books|Business & Personal Finance|Real Estate',
5251             11246 => 'Books|Humor|Jokes & Riddles',
5252             11247 => 'Books|Religion & Spirituality|Comparative Religion',
5253             11255 => 'Books|Cookbooks, Food & Wine|Culinary Arts',
5254             11259 => 'Books|Mysteries & Thrillers|Cozy',
5255             11260 => 'Books|Politics & Current Events|Current Events',
5256             11261 => 'Books|Politics & Current Events|Foreign Policy & International Relations',
5257             11262 => 'Books|Politics & Current Events|Local Government',
5258             11263 => 'Books|Politics & Current Events|National Government',
5259             11264 => 'Books|Politics & Current Events|Political Science',
5260             11265 => 'Books|Politics & Current Events|Public Administration',
5261             11266 => 'Books|Politics & Current Events|World Affairs',
5262             11273 => 'Books|Nonfiction|Family & Relationships|Family & Childcare',
5263             11274 => 'Books|Nonfiction|Family & Relationships|Love & Romance',
5264             11275 => 'Books|Sci-Fi & Fantasy|Fantasy|Urban',
5265             11276 => 'Books|Reference|Foreign Languages|Arabic',
5266             11277 => 'Books|Reference|Foreign Languages|Bilingual Editions',
5267             11278 => 'Books|Reference|Foreign Languages|African Languages',
5268             11279 => 'Books|Reference|Foreign Languages|Ancient Languages',
5269             11280 => 'Books|Reference|Foreign Languages|Chinese',
5270             11281 => 'Books|Reference|Foreign Languages|English',
5271             11282 => 'Books|Reference|Foreign Languages|French',
5272             11283 => 'Books|Reference|Foreign Languages|German',
5273             11284 => 'Books|Reference|Foreign Languages|Hebrew',
5274             11285 => 'Books|Reference|Foreign Languages|Hindi',
5275             11286 => 'Books|Reference|Foreign Languages|Italian',
5276             11287 => 'Books|Reference|Foreign Languages|Japanese',
5277             11288 => 'Books|Reference|Foreign Languages|Korean',
5278             11289 => 'Books|Reference|Foreign Languages|Linguistics',
5279             11290 => 'Books|Reference|Foreign Languages|Other Languages',
5280             11291 => 'Books|Reference|Foreign Languages|Portuguese',
5281             11292 => 'Books|Reference|Foreign Languages|Russian',
5282             11293 => 'Books|Reference|Foreign Languages|Spanish',
5283             11294 => 'Books|Reference|Foreign Languages|Speech Pathology',
5284             11295 => 'Books|Science & Nature|Mathematics|Advanced Mathematics',
5285             11296 => 'Books|Science & Nature|Mathematics|Algebra',
5286             11297 => 'Books|Science & Nature|Mathematics|Arithmetic',
5287             11298 => 'Books|Science & Nature|Mathematics|Calculus',
5288             11299 => 'Books|Science & Nature|Mathematics|Geometry',
5289             11300 => 'Books|Science & Nature|Mathematics|Statistics',
5290             11301 => 'Books|Professional & Technical|Medical|Veterinary',
5291             11302 => 'Books|Professional & Technical|Medical|Neuroscience',
5292             11303 => 'Books|Professional & Technical|Medical|Immunology',
5293             11304 => 'Books|Professional & Technical|Medical|Nursing',
5294             11305 => 'Books|Professional & Technical|Medical|Pharmacology & Toxicology',
5295             11306 => 'Books|Professional & Technical|Medical|Anatomy & Physiology',
5296             11307 => 'Books|Professional & Technical|Medical|Dentistry',
5297             11308 => 'Books|Professional & Technical|Medical|Emergency Medicine',
5298             11309 => 'Books|Professional & Technical|Medical|Genetics',
5299             11310 => 'Books|Professional & Technical|Medical|Psychiatry',
5300             11311 => 'Books|Professional & Technical|Medical|Radiology',
5301             11312 => 'Books|Professional & Technical|Medical|Alternative Medicine',
5302             11317 => 'Books|Nonfiction|Philosophy|Political Philosophy',
5303             11319 => 'Books|Nonfiction|Philosophy|Philosophy of Language',
5304             11320 => 'Books|Nonfiction|Philosophy|Philosophy of Religion',
5305             11327 => 'Books|Nonfiction|Social Science|Sociology',
5306             11329 => 'Books|Professional & Technical|Engineering|Aeronautics',
5307             11330 => 'Books|Professional & Technical|Engineering|Chemical & Petroleum Engineering',
5308             11331 => 'Books|Professional & Technical|Engineering|Civil Engineering',
5309             11332 => 'Books|Professional & Technical|Engineering|Computer Science',
5310             11333 => 'Books|Professional & Technical|Engineering|Electrical Engineering',
5311             11334 => 'Books|Professional & Technical|Engineering|Environmental Engineering',
5312             11335 => 'Books|Professional & Technical|Engineering|Mechanical Engineering',
5313             11336 => 'Books|Professional & Technical|Engineering|Power Resources',
5314             11337 => 'Books|Comics & Graphic Novels|Manga|Boys',
5315             11338 => 'Books|Comics & Graphic Novels|Manga|Men',
5316             11339 => 'Books|Comics & Graphic Novels|Manga|Girls',
5317             11340 => 'Books|Comics & Graphic Novels|Manga|Women',
5318             11341 => 'Books|Comics & Graphic Novels|Manga|Other',
5319             11342 => 'Books|Comics & Graphic Novels|Manga|Yaoi',
5320             11343 => 'Books|Comics & Graphic Novels|Manga|Comic Essays',
5321             12001 => 'Mac App Store|Business',
5322             12002 => 'Mac App Store|Developer Tools',
5323             12003 => 'Mac App Store|Education',
5324             12004 => 'Mac App Store|Entertainment',
5325             12005 => 'Mac App Store|Finance',
5326             12006 => 'Mac App Store|Games',
5327             12007 => 'Mac App Store|Health & Fitness',
5328             12008 => 'Mac App Store|Lifestyle',
5329             12010 => 'Mac App Store|Medical',
5330             12011 => 'Mac App Store|Music',
5331             12012 => 'Mac App Store|News',
5332             12013 => 'Mac App Store|Photography',
5333             12014 => 'Mac App Store|Productivity',
5334             12015 => 'Mac App Store|Reference',
5335             12016 => 'Mac App Store|Social Networking',
5336             12017 => 'Mac App Store|Sports',
5337             12018 => 'Mac App Store|Travel',
5338             12019 => 'Mac App Store|Utilities',
5339             12020 => 'Mac App Store|Video',
5340             12021 => 'Mac App Store|Weather',
5341             12022 => 'Mac App Store|Graphics & Design',
5342             12201 => 'Mac App Store|Games|Action',
5343             12202 => 'Mac App Store|Games|Adventure',
5344             12203 => 'Mac App Store|Games|Casual',
5345             12204 => 'Mac App Store|Games|Board',
5346             12205 => 'Mac App Store|Games|Card',
5347             12206 => 'Mac App Store|Games|Casino',
5348             12207 => 'Mac App Store|Games|Dice',
5349             12208 => 'Mac App Store|Games|Educational',
5350             12209 => 'Mac App Store|Games|Family',
5351             12210 => 'Mac App Store|Games|Kids',
5352             12211 => 'Mac App Store|Games|Music',
5353             12212 => 'Mac App Store|Games|Puzzle',
5354             12213 => 'Mac App Store|Games|Racing',
5355             12214 => 'Mac App Store|Games|Role Playing',
5356             12215 => 'Mac App Store|Games|Simulation',
5357             12216 => 'Mac App Store|Games|Sports',
5358             12217 => 'Mac App Store|Games|Strategy',
5359             12218 => 'Mac App Store|Games|Trivia',
5360             12219 => 'Mac App Store|Games|Word',
5361             13001 => 'App Store|Magazines & Newspapers|News & Politics',
5362             13002 => 'App Store|Magazines & Newspapers|Fashion & Style',
5363             13003 => 'App Store|Magazines & Newspapers|Home & Garden',
5364             13004 => 'App Store|Magazines & Newspapers|Outdoors & Nature',
5365             13005 => 'App Store|Magazines & Newspapers|Sports & Leisure',
5366             13006 => 'App Store|Magazines & Newspapers|Automotive',
5367             13007 => 'App Store|Magazines & Newspapers|Arts & Photography',
5368             13008 => 'App Store|Magazines & Newspapers|Brides & Weddings',
5369             13009 => 'App Store|Magazines & Newspapers|Business & Investing',
5370             13010 => "App Store|Magazines & Newspapers|Children's Magazines",
5371             13011 => 'App Store|Magazines & Newspapers|Computers & Internet',
5372             13012 => 'App Store|Magazines & Newspapers|Cooking, Food & Drink',
5373             13013 => 'App Store|Magazines & Newspapers|Crafts & Hobbies',
5374             13014 => 'App Store|Magazines & Newspapers|Electronics & Audio',
5375             13015 => 'App Store|Magazines & Newspapers|Entertainment',
5376             13017 => 'App Store|Magazines & Newspapers|Health, Mind & Body',
5377             13018 => 'App Store|Magazines & Newspapers|History',
5378             13019 => 'App Store|Magazines & Newspapers|Literary Magazines & Journals',
5379             13020 => "App Store|Magazines & Newspapers|Men's Interest",
5380             13021 => 'App Store|Magazines & Newspapers|Movies & Music',
5381             13023 => 'App Store|Magazines & Newspapers|Parenting & Family',
5382             13024 => 'App Store|Magazines & Newspapers|Pets',
5383             13025 => 'App Store|Magazines & Newspapers|Professional & Trade',
5384             13026 => 'App Store|Magazines & Newspapers|Regional News',
5385             13027 => 'App Store|Magazines & Newspapers|Science',
5386             13028 => 'App Store|Magazines & Newspapers|Teens',
5387             13029 => 'App Store|Magazines & Newspapers|Travel & Regional',
5388             13030 => "App Store|Magazines & Newspapers|Women's Interest",
5389             15000 => 'Textbooks|Arts & Entertainment',
5390             15001 => 'Textbooks|Arts & Entertainment|Art & Architecture',
5391             15002 => 'Textbooks|Arts & Entertainment|Art & Architecture|Urban Planning',
5392             15003 => 'Textbooks|Arts & Entertainment|Art History',
5393             15004 => 'Textbooks|Arts & Entertainment|Dance',
5394             15005 => 'Textbooks|Arts & Entertainment|Design',
5395             15006 => 'Textbooks|Arts & Entertainment|Fashion',
5396             15007 => 'Textbooks|Arts & Entertainment|Film',
5397             15008 => 'Textbooks|Arts & Entertainment|Games',
5398             15009 => 'Textbooks|Arts & Entertainment|Interior Design',
5399             15010 => 'Textbooks|Arts & Entertainment|Media Arts',
5400             15011 => 'Textbooks|Arts & Entertainment|Music',
5401             15012 => 'Textbooks|Arts & Entertainment|Performing Arts',
5402             15013 => 'Textbooks|Arts & Entertainment|Photography',
5403             15014 => 'Textbooks|Arts & Entertainment|Theater',
5404             15015 => 'Textbooks|Arts & Entertainment|TV',
5405             15016 => 'Textbooks|Arts & Entertainment|Visual Arts',
5406             15017 => 'Textbooks|Biographies & Memoirs',
5407             15018 => 'Textbooks|Business & Personal Finance',
5408             15019 => 'Textbooks|Business & Personal Finance|Accounting',
5409             15020 => 'Textbooks|Business & Personal Finance|Careers',
5410             15021 => 'Textbooks|Business & Personal Finance|Economics',
5411             15022 => 'Textbooks|Business & Personal Finance|Finance',
5412             15023 => 'Textbooks|Business & Personal Finance|Hospitality',
5413             15024 => 'Textbooks|Business & Personal Finance|Industries & Professions',
5414             15025 => 'Textbooks|Business & Personal Finance|Investing',
5415             15026 => 'Textbooks|Business & Personal Finance|Management & Leadership',
5416             15027 => 'Textbooks|Business & Personal Finance|Marketing & Sales',
5417             15028 => 'Textbooks|Business & Personal Finance|Personal Finance',
5418             15029 => 'Textbooks|Business & Personal Finance|Real Estate',
5419             15030 => 'Textbooks|Business & Personal Finance|Reference',
5420             15031 => 'Textbooks|Business & Personal Finance|Small Business & Entrepreneurship',
5421             15032 => 'Textbooks|Children & Teens',
5422             15033 => 'Textbooks|Children & Teens|Fiction',
5423             15034 => 'Textbooks|Children & Teens|Nonfiction',
5424             15035 => 'Textbooks|Comics & Graphic Novels',
5425             15036 => 'Textbooks|Comics & Graphic Novels|Graphic Novels',
5426             15037 => 'Textbooks|Comics & Graphic Novels|Manga',
5427             15038 => 'Textbooks|Communications & Media',
5428             15039 => 'Textbooks|Communications & Media|Broadcasting',
5429             15040 => 'Textbooks|Communications & Media|Digital Media',
5430             15041 => 'Textbooks|Communications & Media|Journalism',
5431             15042 => 'Textbooks|Communications & Media|Photojournalism',
5432             15043 => 'Textbooks|Communications & Media|Print',
5433             15044 => 'Textbooks|Communications & Media|Speech',
5434             15045 => 'Textbooks|Communications & Media|Writing',
5435             15046 => 'Textbooks|Computers & Internet',
5436             15047 => 'Textbooks|Computers & Internet|Computers',
5437             15048 => 'Textbooks|Computers & Internet|Databases',
5438             15049 => 'Textbooks|Computers & Internet|Digital Media',
5439             15050 => 'Textbooks|Computers & Internet|Internet',
5440             15051 => 'Textbooks|Computers & Internet|Network',
5441             15052 => 'Textbooks|Computers & Internet|Operating Systems',
5442             15053 => 'Textbooks|Computers & Internet|Programming',
5443             15054 => 'Textbooks|Computers & Internet|Software',
5444             15055 => 'Textbooks|Computers & Internet|System Administration',
5445             15056 => 'Textbooks|Cookbooks, Food & Wine',
5446             15057 => 'Textbooks|Cookbooks, Food & Wine|Beverages',
5447             15058 => 'Textbooks|Cookbooks, Food & Wine|Courses & Dishes',
5448             15059 => 'Textbooks|Cookbooks, Food & Wine|Culinary Arts',
5449             15060 => 'Textbooks|Cookbooks, Food & Wine|Methods',
5450             15061 => 'Textbooks|Cookbooks, Food & Wine|Reference',
5451             15062 => 'Textbooks|Cookbooks, Food & Wine|Regional & Ethnic',
5452             15063 => 'Textbooks|Cookbooks, Food & Wine|Special Diet',
5453             15064 => 'Textbooks|Cookbooks, Food & Wine|Special Occasions',
5454             15065 => 'Textbooks|Cookbooks, Food & Wine|Specific Ingredients',
5455             15066 => 'Textbooks|Engineering',
5456             15067 => 'Textbooks|Engineering|Aeronautics',
5457             15068 => 'Textbooks|Engineering|Chemical & Petroleum Engineering',
5458             15069 => 'Textbooks|Engineering|Civil Engineering',
5459             15070 => 'Textbooks|Engineering|Computer Science',
5460             15071 => 'Textbooks|Engineering|Electrical Engineering',
5461             15072 => 'Textbooks|Engineering|Environmental Engineering',
5462             15073 => 'Textbooks|Engineering|Mechanical Engineering',
5463             15074 => 'Textbooks|Engineering|Power Resources',
5464             15075 => 'Textbooks|Fiction & Literature',
5465             15076 => 'Textbooks|Fiction & Literature|Latino',
5466             15077 => 'Textbooks|Fiction & Literature|Action & Adventure',
5467             15078 => 'Textbooks|Fiction & Literature|African American',
5468             15079 => 'Textbooks|Fiction & Literature|Anthologies',
5469             15080 => 'Textbooks|Fiction & Literature|Classics',
5470             15081 => 'Textbooks|Fiction & Literature|Comparative Literature',
5471             15082 => 'Textbooks|Fiction & Literature|Erotica',
5472             15083 => 'Textbooks|Fiction & Literature|Gay',
5473             15084 => 'Textbooks|Fiction & Literature|Ghost',
5474             15085 => 'Textbooks|Fiction & Literature|Historical',
5475             15086 => 'Textbooks|Fiction & Literature|Horror',
5476             15087 => 'Textbooks|Fiction & Literature|Literary',
5477             15088 => 'Textbooks|Fiction & Literature|Literary Criticism',
5478             15089 => 'Textbooks|Fiction & Literature|Poetry',
5479             15090 => 'Textbooks|Fiction & Literature|Religious',
5480             15091 => 'Textbooks|Fiction & Literature|Short Stories',
5481             15092 => 'Textbooks|Health, Mind & Body',
5482             15093 => 'Textbooks|Health, Mind & Body|Fitness',
5483             15094 => 'Textbooks|Health, Mind & Body|Self-Improvement',
5484             15095 => 'Textbooks|History',
5485             15096 => 'Textbooks|History|Africa',
5486             15097 => 'Textbooks|History|Americas',
5487             15098 => 'Textbooks|History|Americas|Canada',
5488             15099 => 'Textbooks|History|Americas|Latin America',
5489             15100 => 'Textbooks|History|Americas|United States',
5490             15101 => 'Textbooks|History|Ancient',
5491             15102 => 'Textbooks|History|Asia',
5492             15103 => 'Textbooks|History|Australia & Oceania',
5493             15104 => 'Textbooks|History|Europe',
5494             15105 => 'Textbooks|History|Middle East',
5495             15106 => 'Textbooks|History|Military',
5496             15107 => 'Textbooks|History|World',
5497             15108 => 'Textbooks|Humor',
5498             15109 => 'Textbooks|Language Studies',
5499             15110 => 'Textbooks|Language Studies|African Languages',
5500             15111 => 'Textbooks|Language Studies|Ancient Languages',
5501             15112 => 'Textbooks|Language Studies|Arabic',
5502             15113 => 'Textbooks|Language Studies|Bilingual Editions',
5503             15114 => 'Textbooks|Language Studies|Chinese',
5504             15115 => 'Textbooks|Language Studies|English',
5505             15116 => 'Textbooks|Language Studies|French',
5506             15117 => 'Textbooks|Language Studies|German',
5507             15118 => 'Textbooks|Language Studies|Hebrew',
5508             15119 => 'Textbooks|Language Studies|Hindi',
5509             15120 => 'Textbooks|Language Studies|Indigenous Languages',
5510             15121 => 'Textbooks|Language Studies|Italian',
5511             15122 => 'Textbooks|Language Studies|Japanese',
5512             15123 => 'Textbooks|Language Studies|Korean',
5513             15124 => 'Textbooks|Language Studies|Linguistics',
5514             15125 => 'Textbooks|Language Studies|Other Language',
5515             15126 => 'Textbooks|Language Studies|Portuguese',
5516             15127 => 'Textbooks|Language Studies|Russian',
5517             15128 => 'Textbooks|Language Studies|Spanish',
5518             15129 => 'Textbooks|Language Studies|Speech Pathology',
5519             15130 => 'Textbooks|Lifestyle & Home',
5520             15131 => 'Textbooks|Lifestyle & Home|Antiques & Collectibles',
5521             15132 => 'Textbooks|Lifestyle & Home|Crafts & Hobbies',
5522             15133 => 'Textbooks|Lifestyle & Home|Gardening',
5523             15134 => 'Textbooks|Lifestyle & Home|Pets',
5524             15135 => 'Textbooks|Mathematics',
5525             15136 => 'Textbooks|Mathematics|Advanced Mathematics',
5526             15137 => 'Textbooks|Mathematics|Algebra',
5527             15138 => 'Textbooks|Mathematics|Arithmetic',
5528             15139 => 'Textbooks|Mathematics|Calculus',
5529             15140 => 'Textbooks|Mathematics|Geometry',
5530             15141 => 'Textbooks|Mathematics|Statistics',
5531             15142 => 'Textbooks|Medicine',
5532             15143 => 'Textbooks|Medicine|Anatomy & Physiology',
5533             15144 => 'Textbooks|Medicine|Dentistry',
5534             15145 => 'Textbooks|Medicine|Emergency Medicine',
5535             15146 => 'Textbooks|Medicine|Genetics',
5536             15147 => 'Textbooks|Medicine|Immunology',
5537             15148 => 'Textbooks|Medicine|Neuroscience',
5538             15149 => 'Textbooks|Medicine|Nursing',
5539             15150 => 'Textbooks|Medicine|Pharmacology & Toxicology',
5540             15151 => 'Textbooks|Medicine|Psychiatry',
5541             15152 => 'Textbooks|Medicine|Psychology',
5542             15153 => 'Textbooks|Medicine|Radiology',
5543             15154 => 'Textbooks|Medicine|Veterinary',
5544             15155 => 'Textbooks|Mysteries & Thrillers',
5545             15156 => 'Textbooks|Mysteries & Thrillers|British Detectives',
5546             15157 => 'Textbooks|Mysteries & Thrillers|Hard-Boiled',
5547             15158 => 'Textbooks|Mysteries & Thrillers|Historical',
5548             15159 => 'Textbooks|Mysteries & Thrillers|Police Procedural',
5549             15160 => 'Textbooks|Mysteries & Thrillers|Short Stories',
5550             15161 => 'Textbooks|Mysteries & Thrillers|Women Sleuths',
5551             15162 => 'Textbooks|Nonfiction',
5552             15163 => 'Textbooks|Nonfiction|Family & Relationships',
5553             15164 => 'Textbooks|Nonfiction|Transportation',
5554             15165 => 'Textbooks|Nonfiction|True Crime',
5555             15166 => 'Textbooks|Parenting',
5556             15167 => 'Textbooks|Philosophy',
5557             15168 => 'Textbooks|Philosophy|Aesthetics',
5558             15169 => 'Textbooks|Philosophy|Epistemology',
5559             15170 => 'Textbooks|Philosophy|Ethics',
5560             15171 => 'Textbooks|Philosophy|Philosophy of Language',
5561             15172 => 'Textbooks|Philosophy|Logic',
5562             15173 => 'Textbooks|Philosophy|Metaphysics',
5563             15174 => 'Textbooks|Philosophy|Political Philosophy',
5564             15175 => 'Textbooks|Philosophy|Philosophy of Religion',
5565             15176 => 'Textbooks|Politics & Current Events',
5566             15177 => 'Textbooks|Politics & Current Events|Current Events',
5567             15178 => 'Textbooks|Politics & Current Events|Foreign Policy & International Relations',
5568             15179 => 'Textbooks|Politics & Current Events|Local Governments',
5569             15180 => 'Textbooks|Politics & Current Events|National Governments',
5570             15181 => 'Textbooks|Politics & Current Events|Political Science',
5571             15182 => 'Textbooks|Politics & Current Events|Public Administration',
5572             15183 => 'Textbooks|Politics & Current Events|World Affairs',
5573             15184 => 'Textbooks|Professional & Technical',
5574             15185 => 'Textbooks|Professional & Technical|Design',
5575             15186 => 'Textbooks|Professional & Technical|Language Arts & Disciplines',
5576             15187 => 'Textbooks|Professional & Technical|Engineering',
5577             15188 => 'Textbooks|Professional & Technical|Law',
5578             15189 => 'Textbooks|Professional & Technical|Medical',
5579             15190 => 'Textbooks|Reference',
5580             15191 => 'Textbooks|Reference|Almanacs & Yearbooks',
5581             15192 => 'Textbooks|Reference|Atlases & Maps',
5582             15193 => 'Textbooks|Reference|Catalogs & Directories',
5583             15194 => 'Textbooks|Reference|Consumer Guides',
5584             15195 => 'Textbooks|Reference|Dictionaries & Thesauruses',
5585             15196 => 'Textbooks|Reference|Encyclopedias',
5586             15197 => 'Textbooks|Reference|Etiquette',
5587             15198 => 'Textbooks|Reference|Quotations',
5588             15199 => 'Textbooks|Reference|Study Aids',
5589             15200 => 'Textbooks|Reference|Words & Language',
5590             15201 => 'Textbooks|Reference|Writing',
5591             15202 => 'Textbooks|Religion & Spirituality',
5592             15203 => 'Textbooks|Religion & Spirituality|Bible Studies',
5593             15204 => 'Textbooks|Religion & Spirituality|Bibles',
5594             15205 => 'Textbooks|Religion & Spirituality|Buddhism',
5595             15206 => 'Textbooks|Religion & Spirituality|Christianity',
5596             15207 => 'Textbooks|Religion & Spirituality|Comparative Religion',
5597             15208 => 'Textbooks|Religion & Spirituality|Hinduism',
5598             15209 => 'Textbooks|Religion & Spirituality|Islam',
5599             15210 => 'Textbooks|Religion & Spirituality|Judaism',
5600             15211 => 'Textbooks|Religion & Spirituality|Spirituality',
5601             15212 => 'Textbooks|Romance',
5602             15213 => 'Textbooks|Romance|Contemporary',
5603             15214 => 'Textbooks|Romance|Erotic Romance',
5604             15215 => 'Textbooks|Romance|Paranormal',
5605             15216 => 'Textbooks|Romance|Historical',
5606             15217 => 'Textbooks|Romance|Short Stories',
5607             15218 => 'Textbooks|Romance|Suspense',
5608             15219 => 'Textbooks|Romance|Western',
5609             15220 => 'Textbooks|Sci-Fi & Fantasy',
5610             15221 => 'Textbooks|Sci-Fi & Fantasy|Fantasy',
5611             15222 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Contemporary',
5612             15223 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Epic',
5613             15224 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Historical',
5614             15225 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Paranormal',
5615             15226 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Short Stories',
5616             15227 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction',
5617             15228 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature',
5618             15229 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature|Adventure',
5619             15230 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature|High Tech',
5620             15231 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature|Short Stories',
5621             15232 => 'Textbooks|Science & Nature',
5622             15233 => 'Textbooks|Science & Nature|Agriculture',
5623             15234 => 'Textbooks|Science & Nature|Astronomy',
5624             15235 => 'Textbooks|Science & Nature|Atmosphere',
5625             15236 => 'Textbooks|Science & Nature|Biology',
5626             15237 => 'Textbooks|Science & Nature|Chemistry',
5627             15238 => 'Textbooks|Science & Nature|Earth Sciences',
5628             15239 => 'Textbooks|Science & Nature|Ecology',
5629             15240 => 'Textbooks|Science & Nature|Environment',
5630             15241 => 'Textbooks|Science & Nature|Essays',
5631             15242 => 'Textbooks|Science & Nature|Geography',
5632             15243 => 'Textbooks|Science & Nature|Geology',
5633             15244 => 'Textbooks|Science & Nature|History',
5634             15245 => 'Textbooks|Science & Nature|Life Sciences',
5635             15246 => 'Textbooks|Science & Nature|Nature',
5636             15247 => 'Textbooks|Science & Nature|Physics',
5637             15248 => 'Textbooks|Science & Nature|Reference',
5638             15249 => 'Textbooks|Social Science',
5639             15250 => 'Textbooks|Social Science|Anthropology',
5640             15251 => 'Textbooks|Social Science|Archaeology',
5641             15252 => 'Textbooks|Social Science|Civics',
5642             15253 => 'Textbooks|Social Science|Government',
5643             15254 => 'Textbooks|Social Science|Social Studies',
5644             15255 => 'Textbooks|Social Science|Social Welfare',
5645             15256 => 'Textbooks|Social Science|Society',
5646             15257 => 'Textbooks|Social Science|Society|African Studies',
5647             15258 => 'Textbooks|Social Science|Society|American Studies',
5648             15259 => 'Textbooks|Social Science|Society|Asia Pacific Studies',
5649             15260 => 'Textbooks|Social Science|Society|Cross-Cultural Studies',
5650             15261 => 'Textbooks|Social Science|Society|European Studies',
5651             15262 => 'Textbooks|Social Science|Society|Immigration & Emigration',
5652             15263 => 'Textbooks|Social Science|Society|Indigenous Studies',
5653             15264 => 'Textbooks|Social Science|Society|Latin & Caribbean Studies',
5654             15265 => 'Textbooks|Social Science|Society|Middle Eastern Studies',
5655             15266 => 'Textbooks|Social Science|Society|Race & Ethnicity Studies',
5656             15267 => 'Textbooks|Social Science|Society|Sexuality Studies',
5657             15268 => "Textbooks|Social Science|Society|Women's Studies",
5658             15269 => 'Textbooks|Social Science|Sociology',
5659             15270 => 'Textbooks|Sports & Outdoors',
5660             15271 => 'Textbooks|Sports & Outdoors|Baseball',
5661             15272 => 'Textbooks|Sports & Outdoors|Basketball',
5662             15273 => 'Textbooks|Sports & Outdoors|Coaching',
5663             15274 => 'Textbooks|Sports & Outdoors|Equestrian',
5664             15275 => 'Textbooks|Sports & Outdoors|Extreme Sports',
5665             15276 => 'Textbooks|Sports & Outdoors|Football',
5666             15277 => 'Textbooks|Sports & Outdoors|Golf',
5667             15278 => 'Textbooks|Sports & Outdoors|Hockey',
5668             15279 => 'Textbooks|Sports & Outdoors|Motor Sports',
5669             15280 => 'Textbooks|Sports & Outdoors|Mountaineering',
5670             15281 => 'Textbooks|Sports & Outdoors|Outdoors',
5671             15282 => 'Textbooks|Sports & Outdoors|Racket Sports',
5672             15283 => 'Textbooks|Sports & Outdoors|Reference',
5673             15284 => 'Textbooks|Sports & Outdoors|Soccer',
5674             15285 => 'Textbooks|Sports & Outdoors|Training',
5675             15286 => 'Textbooks|Sports & Outdoors|Water Sports',
5676             15287 => 'Textbooks|Sports & Outdoors|Winter Sports',
5677             15288 => 'Textbooks|Teaching & Learning',
5678             15289 => 'Textbooks|Teaching & Learning|Adult Education',
5679             15290 => 'Textbooks|Teaching & Learning|Curriculum & Teaching',
5680             15291 => 'Textbooks|Teaching & Learning|Educational Leadership',
5681             15292 => 'Textbooks|Teaching & Learning|Educational Technology',
5682             15293 => 'Textbooks|Teaching & Learning|Family & Childcare',
5683             15294 => 'Textbooks|Teaching & Learning|Information & Library Science',
5684             15295 => 'Textbooks|Teaching & Learning|Learning Resources',
5685             15296 => 'Textbooks|Teaching & Learning|Psychology & Research',
5686             15297 => 'Textbooks|Teaching & Learning|Special Education',
5687             15298 => 'Textbooks|Travel & Adventure',
5688             15299 => 'Textbooks|Travel & Adventure|Africa',
5689             15300 => 'Textbooks|Travel & Adventure|Americas',
5690             15301 => 'Textbooks|Travel & Adventure|Americas|Canada',
5691             15302 => 'Textbooks|Travel & Adventure|Americas|Latin America',
5692             15303 => 'Textbooks|Travel & Adventure|Americas|United States',
5693             15304 => 'Textbooks|Travel & Adventure|Asia',
5694             15305 => 'Textbooks|Travel & Adventure|Caribbean',
5695             15306 => 'Textbooks|Travel & Adventure|Essays & Memoirs',
5696             15307 => 'Textbooks|Travel & Adventure|Europe',
5697             15308 => 'Textbooks|Travel & Adventure|Middle East',
5698             15309 => 'Textbooks|Travel & Adventure|Oceania',
5699             15310 => 'Textbooks|Travel & Adventure|Specialty Travel',
5700             15311 => 'Textbooks|Comics & Graphic Novels|Comics',
5701             15312 => 'Textbooks|Reference|Manuals',
5702             16001 => 'App Store|Stickers|Emoji & Expressions',
5703             16003 => 'App Store|Stickers|Animals & Nature',
5704             16005 => 'App Store|Stickers|Art',
5705             16006 => 'App Store|Stickers|Celebrations',
5706             16007 => 'App Store|Stickers|Celebrities',
5707             16008 => 'App Store|Stickers|Comics & Cartoons',
5708             16009 => 'App Store|Stickers|Eating & Drinking',
5709             16010 => 'App Store|Stickers|Gaming',
5710             16014 => 'App Store|Stickers|Movies & TV',
5711             16015 => 'App Store|Stickers|Music',
5712             16017 => 'App Store|Stickers|People',
5713             16019 => 'App Store|Stickers|Places & Objects',
5714             16021 => 'App Store|Stickers|Sports & Activities',
5715             16025 => 'App Store|Stickers|Kids & Family',
5716             16026 => 'App Store|Stickers|Fashion',
5717             100000 => 'Music|Christian & Gospel',
5718             100001 => 'Music|Classical|Art Song',
5719             100002 => 'Music|Classical|Brass & Woodwinds',
5720             100003 => 'Music|Classical|Solo Instrumental',
5721             100004 => 'Music|Classical|Contemporary Era',
5722             100005 => 'Music|Classical|Oratorio',
5723             100006 => 'Music|Classical|Cantata',
5724             100007 => 'Music|Classical|Electronic',
5725             100008 => 'Music|Classical|Sacred',
5726             100009 => 'Music|Classical|Guitar',
5727             100010 => 'Music|Classical|Piano',
5728             100011 => 'Music|Classical|Violin',
5729             100012 => 'Music|Classical|Cello',
5730             100013 => 'Music|Classical|Percussion',
5731             100014 => 'Music|Electronic|Dubstep',
5732             100015 => 'Music|Electronic|Bass',
5733             100016 => 'Music|Hip-Hop/Rap|UK Hip-Hop',
5734             100017 => 'Music|Reggae|Lovers Rock',
5735             100018 => 'Music|Alternative|EMO',
5736             100019 => 'Music|Alternative|Pop Punk',
5737             100020 => 'Music|Alternative|Indie Pop',
5738             100021 => 'Music|New Age|Yoga',
5739             100022 => 'Music|Pop|Tribute',
5740             100023 => 'Music|Pop|Shows',
5741             100024 => 'Music|Cuban',
5742             100025 => 'Music|Cuban|Mambo',
5743             100026 => 'Music|Cuban|Chachacha',
5744             100027 => 'Music|Cuban|Guajira',
5745             100028 => 'Music|Cuban|Son',
5746             100029 => 'Music|Cuban|Bolero',
5747             100030 => 'Music|Cuban|Guaracha',
5748             100031 => 'Music|Cuban|Timba',
5749             100032 => 'Music|Soundtrack|Video Game',
5750             100033 => 'Music|Indian|Regional Indian|Punjabi|Punjabi Pop',
5751             100034 => 'Music|Indian|Regional Indian|Bengali|Rabindra Sangeet',
5752             100035 => 'Music|Indian|Regional Indian|Malayalam',
5753             100036 => 'Music|Indian|Regional Indian|Kannada',
5754             100037 => 'Music|Indian|Regional Indian|Marathi',
5755             100038 => 'Music|Indian|Regional Indian|Gujarati',
5756             100039 => 'Music|Indian|Regional Indian|Assamese',
5757             100040 => 'Music|Indian|Regional Indian|Bhojpuri',
5758             100041 => 'Music|Indian|Regional Indian|Haryanvi',
5759             100042 => 'Music|Indian|Regional Indian|Odia',
5760             100043 => 'Music|Indian|Regional Indian|Rajasthani',
5761             100044 => 'Music|Indian|Regional Indian|Urdu',
5762             100045 => 'Music|Indian|Regional Indian|Punjabi',
5763             100046 => 'Music|Indian|Regional Indian|Bengali',
5764             100047 => 'Music|Indian|Indian Classical|Carnatic Classical',
5765             100048 => 'Music|Indian|Indian Classical|Hindustani Classical',
5766             100049 => 'Music|African|Afro House',
5767             100050 => 'Music|African|Afro Soul',
5768             100051 => 'Music|African|Afrobeats',
5769             100052 => 'Music|African|Benga',
5770             100053 => 'Music|African|Bongo-Flava',
5771             100054 => 'Music|African|Coupe-Decale',
5772             100055 => 'Music|African|Gqom',
5773             100056 => 'Music|African|Highlife',
5774             100057 => 'Music|African|Kuduro',
5775             100058 => 'Music|African|Kizomba',
5776             100059 => 'Music|African|Kwaito',
5777             100060 => 'Music|African|Mbalax',
5778             100061 => 'Music|African|Ndombolo',
5779             100062 => 'Music|African|Shangaan Electro',
5780             100063 => 'Music|African|Soukous',
5781             100064 => 'Music|African|Taarab',
5782             100065 => 'Music|African|Zouglou',
5783             100066 => 'Music|Turkish|Ozgun',
5784             100067 => 'Music|Turkish|Fantezi',
5785             100068 => 'Music|Turkish|Religious',
5786             100069 => 'Music|Pop|Turkish Pop',
5787             100070 => 'Music|Rock|Turkish Rock',
5788             100071 => 'Music|Alternative|Turkish Alternative',
5789             100072 => 'Music|Hip-Hop/Rap|Turkish Hip-Hop/Rap',
5790             100073 => 'Music|African|Maskandi',
5791             100074 => 'Music|Russian|Russian Romance',
5792             100075 => 'Music|Russian|Russian Bard',
5793             100076 => 'Music|Russian|Russian Pop',
5794             100077 => 'Music|Russian|Russian Rock',
5795             100078 => 'Music|Russian|Russian Hip-Hop',
5796             100079 => 'Music|Arabic|Levant',
5797             100080 => 'Music|Arabic|Levant|Dabke',
5798             100081 => 'Music|Arabic|Maghreb Rai',
5799             100082 => 'Music|Arabic|Khaleeji|Khaleeji Jalsat',
5800             100083 => 'Music|Arabic|Khaleeji|Khaleeji Shailat',
5801             100084 => 'Music|Tarab',
5802             100085 => 'Music|Tarab|Iraqi Tarab',
5803             100086 => 'Music|Tarab|Egyptian Tarab',
5804             100087 => 'Music|Tarab|Khaleeji Tarab',
5805             100088 => 'Music|Pop|Levant Pop',
5806             100089 => 'Music|Pop|Iraqi Pop',
5807             100090 => 'Music|Pop|Egyptian Pop',
5808             100091 => 'Music|Pop|Maghreb Pop',
5809             100092 => 'Music|Pop|Khaleeji Pop',
5810             100093 => 'Music|Hip-Hop/Rap|Levant Hip-Hop',
5811             100094 => 'Music|Hip-Hop/Rap|Egyptian Hip-Hop',
5812             100095 => 'Music|Hip-Hop/Rap|Maghreb Hip-Hop',
5813             100096 => 'Music|Hip-Hop/Rap|Khaleeji Hip-Hop',
5814             100097 => 'Music|Alternative|Indie Levant',
5815             100098 => 'Music|Alternative|Indie Egyptian',
5816             100099 => 'Music|Alternative|Indie Maghreb',
5817             100100 => 'Music|Electronic|Levant Electronic',
5818             100101 => "Music|Electronic|Electro-Cha'abi",
5819             100102 => 'Music|Electronic|Maghreb Electronic',
5820             100103 => 'Music|Folk|Iraqi Folk',
5821             100104 => 'Music|Folk|Khaleeji Folk',
5822             100105 => 'Music|Dance|Maghreb Dance',
5823             40000000 => 'iTunes U',
5824             40000001 => 'iTunes U|Business & Economics',
5825             40000002 => 'iTunes U|Business & Economics|Economics',
5826             40000003 => 'iTunes U|Business & Economics|Finance',
5827             40000004 => 'iTunes U|Business & Economics|Hospitality',
5828             40000005 => 'iTunes U|Business & Economics|Management',
5829             40000006 => 'iTunes U|Business & Economics|Marketing',
5830             40000007 => 'iTunes U|Business & Economics|Personal Finance',
5831             40000008 => 'iTunes U|Business & Economics|Real Estate',
5832             40000009 => 'iTunes U|Engineering',
5833             40000010 => 'iTunes U|Engineering|Chemical & Petroleum Engineering',
5834             40000011 => 'iTunes U|Engineering|Civil Engineering',
5835             40000012 => 'iTunes U|Engineering|Computer Science',
5836             40000013 => 'iTunes U|Engineering|Electrical Engineering',
5837             40000014 => 'iTunes U|Engineering|Environmental Engineering',
5838             40000015 => 'iTunes U|Engineering|Mechanical Engineering',
5839             40000016 => 'iTunes U|Music, Art, & Design',
5840             40000017 => 'iTunes U|Music, Art, & Design|Architecture',
5841             40000019 => 'iTunes U|Music, Art, & Design|Art History',
5842             40000020 => 'iTunes U|Music, Art, & Design|Dance',
5843             40000021 => 'iTunes U|Music, Art, & Design|Film',
5844             40000022 => 'iTunes U|Music, Art, & Design|Design',
5845             40000023 => 'iTunes U|Music, Art, & Design|Interior Design',
5846             40000024 => 'iTunes U|Music, Art, & Design|Music',
5847             40000025 => 'iTunes U|Music, Art, & Design|Theater',
5848             40000026 => 'iTunes U|Health & Medicine',
5849             40000027 => 'iTunes U|Health & Medicine|Anatomy & Physiology',
5850             40000028 => 'iTunes U|Health & Medicine|Behavioral Science',
5851             40000029 => 'iTunes U|Health & Medicine|Dentistry',
5852             40000030 => 'iTunes U|Health & Medicine|Diet & Nutrition',
5853             40000031 => 'iTunes U|Health & Medicine|Emergency Medicine',
5854             40000032 => 'iTunes U|Health & Medicine|Genetics',
5855             40000033 => 'iTunes U|Health & Medicine|Gerontology',
5856             40000034 => 'iTunes U|Health & Medicine|Health & Exercise Science',
5857             40000035 => 'iTunes U|Health & Medicine|Immunology',
5858             40000036 => 'iTunes U|Health & Medicine|Neuroscience',
5859             40000037 => 'iTunes U|Health & Medicine|Pharmacology & Toxicology',
5860             40000038 => 'iTunes U|Health & Medicine|Psychiatry',
5861             40000039 => 'iTunes U|Health & Medicine|Global Health',
5862             40000040 => 'iTunes U|Health & Medicine|Radiology',
5863             40000041 => 'iTunes U|History',
5864             40000042 => 'iTunes U|History|Ancient History',
5865             40000043 => 'iTunes U|History|Medieval History',
5866             40000044 => 'iTunes U|History|Military History',
5867             40000045 => 'iTunes U|History|Modern History',
5868             40000046 => 'iTunes U|History|African History',
5869             40000047 => 'iTunes U|History|Asia-Pacific History',
5870             40000048 => 'iTunes U|History|European History',
5871             40000049 => 'iTunes U|History|Middle Eastern History',
5872             40000050 => 'iTunes U|History|North American History',
5873             40000051 => 'iTunes U|History|South American History',
5874             40000053 => 'iTunes U|Communications & Journalism',
5875             40000054 => 'iTunes U|Philosophy',
5876             40000055 => 'iTunes U|Religion & Spirituality',
5877             40000056 => 'iTunes U|Languages',
5878             40000057 => 'iTunes U|Languages|African Languages',
5879             40000058 => 'iTunes U|Languages|Ancient Languages',
5880             40000061 => 'iTunes U|Languages|English',
5881             40000063 => 'iTunes U|Languages|French',
5882             40000064 => 'iTunes U|Languages|German',
5883             40000065 => 'iTunes U|Languages|Italian',
5884             40000066 => 'iTunes U|Languages|Linguistics',
5885             40000068 => 'iTunes U|Languages|Spanish',
5886             40000069 => 'iTunes U|Languages|Speech Pathology',
5887             40000070 => 'iTunes U|Writing & Literature',
5888             40000071 => 'iTunes U|Writing & Literature|Anthologies',
5889             40000072 => 'iTunes U|Writing & Literature|Biography',
5890             40000073 => 'iTunes U|Writing & Literature|Classics',
5891             40000074 => 'iTunes U|Writing & Literature|Literary Criticism',
5892             40000075 => 'iTunes U|Writing & Literature|Fiction',
5893             40000076 => 'iTunes U|Writing & Literature|Poetry',
5894             40000077 => 'iTunes U|Mathematics',
5895             40000078 => 'iTunes U|Mathematics|Advanced Mathematics',
5896             40000079 => 'iTunes U|Mathematics|Algebra',
5897             40000080 => 'iTunes U|Mathematics|Arithmetic',
5898             40000081 => 'iTunes U|Mathematics|Calculus',
5899             40000082 => 'iTunes U|Mathematics|Geometry',
5900             40000083 => 'iTunes U|Mathematics|Statistics',
5901             40000084 => 'iTunes U|Science',
5902             40000085 => 'iTunes U|Science|Agricultural',
5903             40000086 => 'iTunes U|Science|Astronomy',
5904             40000087 => 'iTunes U|Science|Atmosphere',
5905             40000088 => 'iTunes U|Science|Biology',
5906             40000089 => 'iTunes U|Science|Chemistry',
5907             40000090 => 'iTunes U|Science|Ecology',
5908             40000091 => 'iTunes U|Science|Geography',
5909             40000092 => 'iTunes U|Science|Geology',
5910             40000093 => 'iTunes U|Science|Physics',
5911             40000094 => 'iTunes U|Social Science',
5912             40000095 => 'iTunes U|Law & Politics|Law',
5913             40000096 => 'iTunes U|Law & Politics|Political Science',
5914             40000097 => 'iTunes U|Law & Politics|Public Administration',
5915             40000098 => 'iTunes U|Social Science|Psychology',
5916             40000099 => 'iTunes U|Social Science|Social Welfare',
5917             40000100 => 'iTunes U|Social Science|Sociology',
5918             40000101 => 'iTunes U|Society',
5919             40000103 => 'iTunes U|Society|Asia Pacific Studies',
5920             40000104 => 'iTunes U|Society|European Studies',
5921             40000105 => 'iTunes U|Society|Indigenous Studies',
5922             40000106 => 'iTunes U|Society|Latin & Caribbean Studies',
5923             40000107 => 'iTunes U|Society|Middle Eastern Studies',
5924             40000108 => "iTunes U|Society|Women's Studies",
5925             40000109 => 'iTunes U|Teaching & Learning',
5926             40000110 => 'iTunes U|Teaching & Learning|Curriculum & Teaching',
5927             40000111 => 'iTunes U|Teaching & Learning|Educational Leadership',
5928             40000112 => 'iTunes U|Teaching & Learning|Family & Childcare',
5929             40000113 => 'iTunes U|Teaching & Learning|Learning Resources',
5930             40000114 => 'iTunes U|Teaching & Learning|Psychology & Research',
5931             40000115 => 'iTunes U|Teaching & Learning|Special Education',
5932             40000116 => 'iTunes U|Music, Art, & Design|Culinary Arts',
5933             40000117 => 'iTunes U|Music, Art, & Design|Fashion',
5934             40000118 => 'iTunes U|Music, Art, & Design|Media Arts',
5935             40000119 => 'iTunes U|Music, Art, & Design|Photography',
5936             40000120 => 'iTunes U|Music, Art, & Design|Visual Art',
5937             40000121 => 'iTunes U|Business & Economics|Entrepreneurship',
5938             40000122 => 'iTunes U|Communications & Journalism|Broadcasting',
5939             40000123 => 'iTunes U|Communications & Journalism|Digital Media',
5940             40000124 => 'iTunes U|Communications & Journalism|Journalism',
5941             40000125 => 'iTunes U|Communications & Journalism|Photojournalism',
5942             40000126 => 'iTunes U|Communications & Journalism|Print',
5943             40000127 => 'iTunes U|Communications & Journalism|Speech',
5944             40000128 => 'iTunes U|Communications & Journalism|Writing',
5945             40000129 => 'iTunes U|Health & Medicine|Nursing',
5946             40000130 => 'iTunes U|Languages|Arabic',
5947             40000131 => 'iTunes U|Languages|Chinese',
5948             40000132 => 'iTunes U|Languages|Hebrew',
5949             40000133 => 'iTunes U|Languages|Hindi',
5950             40000134 => 'iTunes U|Languages|Indigenous Languages',
5951             40000135 => 'iTunes U|Languages|Japanese',
5952             40000136 => 'iTunes U|Languages|Korean',
5953             40000137 => 'iTunes U|Languages|Other Languages',
5954             40000138 => 'iTunes U|Languages|Portuguese',
5955             40000139 => 'iTunes U|Languages|Russian',
5956             40000140 => 'iTunes U|Law & Politics',
5957             40000141 => 'iTunes U|Law & Politics|Foreign Policy & International Relations',
5958             40000142 => 'iTunes U|Law & Politics|Local Governments',
5959             40000143 => 'iTunes U|Law & Politics|National Governments',
5960             40000144 => 'iTunes U|Law & Politics|World Affairs',
5961             40000145 => 'iTunes U|Writing & Literature|Comparative Literature',
5962             40000146 => 'iTunes U|Philosophy|Aesthetics',
5963             40000147 => 'iTunes U|Philosophy|Epistemology',
5964             40000148 => 'iTunes U|Philosophy|Ethics',
5965             40000149 => 'iTunes U|Philosophy|Metaphysics',
5966             40000150 => 'iTunes U|Philosophy|Political Philosophy',
5967             40000151 => 'iTunes U|Philosophy|Logic',
5968             40000152 => 'iTunes U|Philosophy|Philosophy of Language',
5969             40000153 => 'iTunes U|Philosophy|Philosophy of Religion',
5970             40000154 => 'iTunes U|Social Science|Archaeology',
5971             40000155 => 'iTunes U|Social Science|Anthropology',
5972             40000156 => 'iTunes U|Religion & Spirituality|Buddhism',
5973             40000157 => 'iTunes U|Religion & Spirituality|Christianity',
5974             40000158 => 'iTunes U|Religion & Spirituality|Comparative Religion',
5975             40000159 => 'iTunes U|Religion & Spirituality|Hinduism',
5976             40000160 => 'iTunes U|Religion & Spirituality|Islam',
5977             40000161 => 'iTunes U|Religion & Spirituality|Judaism',
5978             40000162 => 'iTunes U|Religion & Spirituality|Other Religions',
5979             40000163 => 'iTunes U|Religion & Spirituality|Spirituality',
5980             40000164 => 'iTunes U|Science|Environment',
5981             40000165 => 'iTunes U|Society|African Studies',
5982             40000166 => 'iTunes U|Society|American Studies',
5983             40000167 => 'iTunes U|Society|Cross-cultural Studies',
5984             40000168 => 'iTunes U|Society|Immigration & Emigration',
5985             40000169 => 'iTunes U|Society|Race & Ethnicity Studies',
5986             40000170 => 'iTunes U|Society|Sexuality Studies',
5987             40000171 => 'iTunes U|Teaching & Learning|Educational Technology',
5988             40000172 => 'iTunes U|Teaching & Learning|Information/Library Science',
5989             40000173 => 'iTunes U|Languages|Dutch',
5990             40000174 => 'iTunes U|Languages|Luxembourgish',
5991             40000175 => 'iTunes U|Languages|Swedish',
5992             40000176 => 'iTunes U|Languages|Norwegian',
5993             40000177 => 'iTunes U|Languages|Finnish',
5994             40000178 => 'iTunes U|Languages|Danish',
5995             40000179 => 'iTunes U|Languages|Polish',
5996             40000180 => 'iTunes U|Languages|Turkish',
5997             40000181 => 'iTunes U|Languages|Flemish',
5998             50000024 => 'Audiobooks',
5999             50000040 => 'Audiobooks|Fiction',
6000             50000041 => 'Audiobooks|Arts & Entertainment',
6001             50000042 => 'Audiobooks|Biographies & Memoirs',
6002             50000043 => 'Audiobooks|Business & Personal Finance',
6003             50000044 => 'Audiobooks|Kids & Young Adults',
6004             50000045 => 'Audiobooks|Classics',
6005             50000046 => 'Audiobooks|Comedy',
6006             50000047 => 'Audiobooks|Drama & Poetry',
6007             50000048 => 'Audiobooks|Speakers & Storytellers',
6008             50000049 => 'Audiobooks|History',
6009             50000050 => 'Audiobooks|Languages',
6010             50000051 => 'Audiobooks|Mysteries & Thrillers',
6011             50000052 => 'Audiobooks|Nonfiction',
6012             50000053 => 'Audiobooks|Religion & Spirituality',
6013             50000054 => 'Audiobooks|Science & Nature',
6014             50000055 => 'Audiobooks|Sci Fi & Fantasy',
6015             50000056 => 'Audiobooks|Self-Development',
6016             50000057 => 'Audiobooks|Sports & Outdoors',
6017             50000058 => 'Audiobooks|Technology',
6018             50000059 => 'Audiobooks|Travel & Adventure',
6019             50000061 => 'Music|Spoken Word',
6020             50000063 => 'Music|Disney',
6021             50000064 => 'Music|French Pop',
6022             50000066 => 'Music|German Pop',
6023             50000068 => 'Music|German Folk',
6024             50000069 => 'Audiobooks|Romance',
6025             50000070 => 'Audiobooks|Audiobooks Latino',
6026             50000071 => 'Books|Comics & Graphic Novels|Manga|Action',
6027             50000072 => 'Books|Comics & Graphic Novels|Manga|Comedy',
6028             50000073 => 'Books|Comics & Graphic Novels|Manga|Erotica',
6029             50000074 => 'Books|Comics & Graphic Novels|Manga|Fantasy',
6030             50000075 => 'Books|Comics & Graphic Novels|Manga|Four Cell Manga',
6031             50000076 => 'Books|Comics & Graphic Novels|Manga|Gay & Lesbian',
6032             50000077 => 'Books|Comics & Graphic Novels|Manga|Hard-Boiled',
6033             50000078 => 'Books|Comics & Graphic Novels|Manga|Heroes',
6034             50000079 => 'Books|Comics & Graphic Novels|Manga|Historical Fiction',
6035             50000080 => 'Books|Comics & Graphic Novels|Manga|Mecha',
6036             50000081 => 'Books|Comics & Graphic Novels|Manga|Mystery',
6037             50000082 => 'Books|Comics & Graphic Novels|Manga|Nonfiction',
6038             50000083 => 'Books|Comics & Graphic Novels|Manga|Religious',
6039             50000084 => 'Books|Comics & Graphic Novels|Manga|Romance',
6040             50000085 => 'Books|Comics & Graphic Novels|Manga|Romantic Comedy',
6041             50000086 => 'Books|Comics & Graphic Novels|Manga|Science Fiction',
6042             50000087 => 'Books|Comics & Graphic Novels|Manga|Sports',
6043             50000088 => 'Books|Fiction & Literature|Light Novels',
6044             50000089 => 'Books|Comics & Graphic Novels|Manga|Horror',
6045             50000090 => 'Books|Comics & Graphic Novels|Comics',
6046             50000091 => 'Books|Romance|Multicultural',
6047             50000092 => 'Audiobooks|Erotica',
6048             50000093 => 'Audiobooks|Light Novels',
6049             },
6050             },
6051             grup => { Name => 'Grouping', Avoid => 1 }, #10
6052             hdvd => { #10
6053             Name => 'HDVideo',
6054             Format => 'int8u', #24
6055             Writable => 'int8s', #27
6056             PrintConv => { 0 => 'No', 1 => 'Yes' },
6057             },
6058             keyw => 'Keyword', #7
6059             ldes => 'LongDescription', #10
6060             pcst => { #7
6061             Name => 'Podcast',
6062             Format => 'int8u', #23
6063             Writable => 'int8s', #27
6064             PrintConv => { 0 => 'No', 1 => 'Yes' },
6065             },
6066             perf => 'Performer',
6067             plID => { #10 (or TV season)
6068             Name => 'PlayListID',
6069             Format => 'int8u', # actually int64u, but split it up
6070             Count => 8,
6071             Writable => 'int32s', #27
6072             },
6073             purd => 'PurchaseDate', #7
6074             purl => 'PodcastURL', #7
6075             rtng => { #10
6076             Name => 'Rating',
6077             Format => 'int8u', #23
6078             Writable => 'int8s', #27
6079             PrintConv => {
6080             0 => 'none',
6081             1 => 'Explicit',
6082             2 => 'Clean',
6083             4 => 'Explicit (old)',
6084             },
6085             },
6086             sfID => { #10
6087             Name => 'AppleStoreCountry',
6088             Format => 'int32u',
6089             Writable => 'int32s', #27
6090             SeparateTable => 1,
6091             PrintConv => { #21
6092             143441 => 'United States', # US
6093             143442 => 'France', # FR
6094             143443 => 'Germany', # DE
6095             143444 => 'United Kingdom', # GB
6096             143445 => 'Austria', # AT
6097             143446 => 'Belgium', # BE
6098             143447 => 'Finland', # FI
6099             143448 => 'Greece', # GR
6100             143449 => 'Ireland', # IE
6101             143450 => 'Italy', # IT
6102             143451 => 'Luxembourg', # LU
6103             143452 => 'Netherlands', # NL
6104             143453 => 'Portugal', # PT
6105             143454 => 'Spain', # ES
6106             143455 => 'Canada', # CA
6107             143456 => 'Sweden', # SE
6108             143457 => 'Norway', # NO
6109             143458 => 'Denmark', # DK
6110             143459 => 'Switzerland', # CH
6111             143460 => 'Australia', # AU
6112             143461 => 'New Zealand', # NZ
6113             143462 => 'Japan', # JP
6114             143463 => 'Hong Kong', # HK
6115             143464 => 'Singapore', # SG
6116             143465 => 'China', # CN
6117             143466 => 'Republic of Korea', # KR
6118             143467 => 'India', # IN
6119             143468 => 'Mexico', # MX
6120             143469 => 'Russia', # RU
6121             143470 => 'Taiwan', # TW
6122             143471 => 'Vietnam', # VN
6123             143472 => 'South Africa', # ZA
6124             143473 => 'Malaysia', # MY
6125             143474 => 'Philippines', # PH
6126             143475 => 'Thailand', # TH
6127             143476 => 'Indonesia', # ID
6128             143477 => 'Pakistan', # PK
6129             143478 => 'Poland', # PL
6130             143479 => 'Saudi Arabia', # SA
6131             143480 => 'Turkey', # TR
6132             143481 => 'United Arab Emirates', # AE
6133             143482 => 'Hungary', # HU
6134             143483 => 'Chile', # CL
6135             143484 => 'Nepal', # NP
6136             143485 => 'Panama', # PA
6137             143486 => 'Sri Lanka', # LK
6138             143487 => 'Romania', # RO
6139             143489 => 'Czech Republic', # CZ
6140             143491 => 'Israel', # IL
6141             143492 => 'Ukraine', # UA
6142             143493 => 'Kuwait', # KW
6143             143494 => 'Croatia', # HR
6144             143495 => 'Costa Rica', # CR
6145             143496 => 'Slovakia', # SK
6146             143497 => 'Lebanon', # LB
6147             143498 => 'Qatar', # QA
6148             143499 => 'Slovenia', # SI
6149             143501 => 'Colombia', # CO
6150             143502 => 'Venezuela', # VE
6151             143503 => 'Brazil', # BR
6152             143504 => 'Guatemala', # GT
6153             143505 => 'Argentina', # AR
6154             143506 => 'El Salvador', # SV
6155             143507 => 'Peru', # PE
6156             143508 => 'Dominican Republic', # DO
6157             143509 => 'Ecuador', # EC
6158             143510 => 'Honduras', # HN
6159             143511 => 'Jamaica', # JM
6160             143512 => 'Nicaragua', # NI
6161             143513 => 'Paraguay', # PY
6162             143514 => 'Uruguay', # UY
6163             143515 => 'Macau', # MO
6164             143516 => 'Egypt', # EG
6165             143517 => 'Kazakhstan', # KZ
6166             143518 => 'Estonia', # EE
6167             143519 => 'Latvia', # LV
6168             143520 => 'Lithuania', # LT
6169             143521 => 'Malta', # MT
6170             143523 => 'Moldova', # MD
6171             143524 => 'Armenia', # AM
6172             143525 => 'Botswana', # BW
6173             143526 => 'Bulgaria', # BG
6174             143528 => 'Jordan', # JO
6175             143529 => 'Kenya', # KE
6176             143530 => 'Macedonia', # MK
6177             143531 => 'Madagascar', # MG
6178             143532 => 'Mali', # ML
6179             143533 => 'Mauritius', # MU
6180             143534 => 'Niger', # NE
6181             143535 => 'Senegal', # SN
6182             143536 => 'Tunisia', # TN
6183             143537 => 'Uganda', # UG
6184             143538 => 'Anguilla', # AI
6185             143539 => 'Bahamas', # BS
6186             143540 => 'Antigua and Barbuda', # AG
6187             143541 => 'Barbados', # BB
6188             143542 => 'Bermuda', # BM
6189             143543 => 'British Virgin Islands', # VG
6190             143544 => 'Cayman Islands', # KY
6191             143545 => 'Dominica', # DM
6192             143546 => 'Grenada', # GD
6193             143547 => 'Montserrat', # MS
6194             143548 => 'St. Kitts and Nevis', # KN
6195             143549 => 'St. Lucia', # LC
6196             143550 => 'St. Vincent and The Grenadines', # VC
6197             143551 => 'Trinidad and Tobago', # TT
6198             143552 => 'Turks and Caicos', # TC
6199             143553 => 'Guyana', # GY
6200             143554 => 'Suriname', # SR
6201             143555 => 'Belize', # BZ
6202             143556 => 'Bolivia', # BO
6203             143557 => 'Cyprus', # CY
6204             143558 => 'Iceland', # IS
6205             143559 => 'Bahrain', # BH
6206             143560 => 'Brunei Darussalam', # BN
6207             143561 => 'Nigeria', # NG
6208             143562 => 'Oman', # OM
6209             143563 => 'Algeria', # DZ
6210             143564 => 'Angola', # AO
6211             143565 => 'Belarus', # BY
6212             143566 => 'Uzbekistan', # UZ
6213             143568 => 'Azerbaijan', # AZ
6214             143571 => 'Yemen', # YE
6215             143572 => 'Tanzania', # TZ
6216             143573 => 'Ghana', # GH
6217             143575 => 'Albania', # AL
6218             143576 => 'Benin', # BJ
6219             143577 => 'Bhutan', # BT
6220             143578 => 'Burkina Faso', # BF
6221             143579 => 'Cambodia', # KH
6222             143580 => 'Cape Verde', # CV
6223             143581 => 'Chad', # TD
6224             143582 => 'Republic of the Congo', # CG
6225             143583 => 'Fiji', # FJ
6226             143584 => 'Gambia', # GM
6227             143585 => 'Guinea-Bissau', # GW
6228             143586 => 'Kyrgyzstan', # KG
6229             143587 => "Lao People's Democratic Republic", # LA
6230             143588 => 'Liberia', # LR
6231             143589 => 'Malawi', # MW
6232             143590 => 'Mauritania', # MR
6233             143591 => 'Federated States of Micronesia', # FM
6234             143592 => 'Mongolia', # MN
6235             143593 => 'Mozambique', # MZ
6236             143594 => 'Namibia', # NA
6237             143595 => 'Palau', # PW
6238             143597 => 'Papua New Guinea', # PG
6239             143598 => 'Sao Tome and Principe', # ST (São Tomé and Príncipe)
6240             143599 => 'Seychelles', # SC
6241             143600 => 'Sierra Leone', # SL
6242             143601 => 'Solomon Islands', # SB
6243             143602 => 'Swaziland', # SZ
6244             143603 => 'Tajikistan', # TJ
6245             143604 => 'Turkmenistan', # TM
6246             143605 => 'Zimbabwe', # ZW
6247             },
6248             },
6249             soaa => 'SortAlbumArtist', #10
6250             soal => 'SortAlbum', #10
6251             soar => 'SortArtist', #10
6252             soco => 'SortComposer', #10
6253             sonm => 'SortName', #10
6254             sosn => 'SortShow', #10
6255             stik => { #10
6256             Name => 'MediaType',
6257             Format => 'int8u', #23
6258             Writable => 'int8s', #27
6259             PrintConvColumns => 2,
6260             PrintConv => { #(http://weblog.xanga.com/gryphondwb/615474010/iphone-ringtones---what-did-itunes-741-really-do.html)
6261             0 => 'Movie (old)', #forum9059 (was Movie)
6262             1 => 'Normal (Music)',
6263             2 => 'Audiobook',
6264             5 => 'Whacked Bookmark',
6265             6 => 'Music Video',
6266             9 => 'Movie', #forum9059 (was Short Film)
6267             10 => 'TV Show',
6268             11 => 'Booklet',
6269             14 => 'Ringtone',
6270             21 => 'Podcast', #15
6271             23 => 'iTunes U', #forum9059
6272             },
6273             },
6274             rate => 'RatingPercent', #PH
6275             titl => { Name => 'Title', Avoid => 1 },
6276             tven => 'TVEpisodeID', #7
6277             tves => { #7/10
6278             Name => 'TVEpisode',
6279             Format => 'int32u',
6280             Writable => 'int32s', #27
6281             },
6282             tvnn => 'TVNetworkName', #7
6283             tvsh => 'TVShow', #10
6284             tvsn => { #7/10
6285             Name => 'TVSeason',
6286             Format => 'int32u',
6287             },
6288             yrrc => 'Year', #(ffmpeg source)
6289             itnu => { #PH (iTunes 10.5)
6290             Name => 'iTunesU',
6291             Format => 'int8u', #27
6292             Writable => 'int8s', #27
6293             Description => 'iTunes U',
6294             PrintConv => { 0 => 'No', 1 => 'Yes' },
6295             },
6296             #https://github.com/communitymedia/mediautilities/blob/master/src/net/sourceforge/jaad/mp4/boxes/BoxTypes.java
6297             gshh => { Name => 'GoogleHostHeader', Format => 'string' },
6298             gspm => { Name => 'GooglePingMessage', Format => 'string' },
6299             gspu => { Name => 'GooglePingURL', Format => 'string' },
6300             gssd => { Name => 'GoogleSourceData', Format => 'string' },
6301             gsst => { Name => 'GoogleStartTime', Format => 'string' },
6302             gstd => {
6303             Name => 'GoogleTrackDuration',
6304             Format => 'string',
6305             ValueConv => '$val / 1000',
6306             ValueConvInv => '$val * 1000',
6307             PrintConv => 'ConvertDuration($val)',
6308             PrintConvInv => q{
6309             $val =~ s/ s$//;
6310             my @a = split /(:| days )/, $val;
6311             my $sign = ($val =~ s/^-//) ? -1 : 1;
6312             $a[0] += shift(@a) * 24 if @a == 4;
6313             $a[0] += shift(@a) * 60 while @a > 1;
6314             return $a[0] * $sign;
6315             },
6316             },
6317              
6318             # atoms observed in AAX audiobooks (ref PH)
6319             "\xa9cpy" => { Name => 'Copyright', Avoid => 1, Groups => { 2 => 'Author' } },
6320             "\xa9pub" => 'Publisher',
6321             "\xa9nrt" => 'Narrator',
6322             '@pti' => 'ParentTitle', # (guess -- same as "\xa9nam")
6323             '@PST' => 'ParentShortTitle', # (guess -- same as "\xa9nam")
6324             '@ppi' => 'ParentProductID', # (guess -- same as 'prID')
6325             '@sti' => 'ShortTitle', # (guess -- same as "\xa9nam")
6326             prID => 'ProductID',
6327             rldt => { Name => 'ReleaseDate', Groups => { 2 => 'Time' }},
6328             CDEK => { Name => 'Unknown_CDEK', Unknown => 1 }, # eg: "B004ZMTFEG" - used in URL's ("asin=")
6329             CDET => { Name => 'Unknown_CDET', Unknown => 1 }, # eg: "ADBL"
6330             VERS => 'ProductVersion',
6331             GUID => 'GUID',
6332             AACR => { Name => 'Unknown_AACR', Unknown => 1 }, # eg: "CR!1T1H1QH6WX7T714G2BMFX3E9MC4S"
6333             # ausr - 30 bytes (User Alias?)
6334             "\xa9xyz" => { #PH (written by Google Photos)
6335             Name => 'GPSCoordinates',
6336             Groups => { 2 => 'Location' },
6337             ValueConv => \&ConvertISO6709,
6338             ValueConvInv => \&ConvInvISO6709,
6339             PrintConv => \&PrintGPSCoordinates,
6340             PrintConvInv => \&PrintInvGPSCoordinates,
6341             },
6342             # the following tags written by iTunes 12.5.1.21
6343             # (ref https://www.ventismedia.com/mantis/view.php?id=14963
6344             # https://community.mp3tag.de/t/x-mp4-new-tag-problems/19488)
6345             "\xa9wrk" => 'Work', #PH
6346             "\xa9mvn" => 'MovementName', #PH
6347             "\xa9mvi" => { #PH
6348             Name => 'MovementNumber',
6349             Format => 'int16u', #27
6350             Writable => 'int16s', #27
6351             },
6352             "\xa9mvc" => { #PH
6353             Name => 'MovementCount',
6354             Format => 'int16u', #27
6355             Writable => 'int16s', #27
6356             },
6357             shwm => { #PH
6358             Name => 'ShowMovement',
6359             Format => 'int8u', #27
6360             Writable => 'int8s', #27
6361             PrintConv => { 0 => 'No', 1 => 'Yes' },
6362             },
6363             );
6364              
6365             # tag decoded from timed face records
6366             %Image::ExifTool::QuickTime::FaceInfo = (
6367             PROCESS_PROC => \&ProcessMOV,
6368             GROUPS => { 2 => 'Video' },
6369             crec => {
6370             Name => 'FaceRec',
6371             SubDirectory => {
6372             TagTable => 'Image::ExifTool::QuickTime::FaceRec',
6373             },
6374             },
6375             );
6376              
6377             # tag decoded from timed face records
6378             %Image::ExifTool::QuickTime::FaceRec = (
6379             PROCESS_PROC => \&ProcessMOV,
6380             GROUPS => { 2 => 'Video' },
6381             cits => {
6382             Name => 'FaceItem',
6383             SubDirectory => {
6384             TagTable => 'Image::ExifTool::QuickTime::Keys',
6385             ProcessProc => \&Process_mebx,
6386             },
6387             },
6388             );
6389              
6390             # item list keys (ref PH)
6391             %Image::ExifTool::QuickTime::Keys = (
6392             PROCESS_PROC => \&ProcessKeys,
6393             WRITE_PROC => \&WriteKeys,
6394             CHECK_PROC => \&CheckQTValue,
6395             VARS => { LONG_TAGS => 7 },
6396             WRITABLE => 1,
6397             # (not PREFERRED when writing)
6398             GROUPS => { 1 => 'Keys' },
6399             WRITE_GROUP => 'Keys',
6400             LANG_INFO => \&GetLangInfo,
6401             NOTES => q{
6402             This directory contains a list of key names which are used to decode tags
6403             written by the "mdta" handler. Also in this table are a few tags found in
6404             timed metadata that are not yet writable by ExifTool. The prefix of
6405             "com.apple.quicktime." has been removed from the TagID's below. These tags
6406             support alternate languages in the same way as the
6407             L tags. Note
6408             that by default,
6409             L and
6410             L tags are
6411             preferred when writing, so to create a tag when a same-named tag exists in
6412             either of these tables, either the "Keys" location must be specified (eg.
6413             C<-Keys:Author=Phil> on the command line), or the PREFERRED level must be
6414             changed via L.
6415             },
6416             version => 'Version',
6417             album => 'Album',
6418             artist => { },
6419             artwork => { },
6420             author => { Name => 'Author', Groups => { 2 => 'Author' } },
6421             comment => { },
6422             copyright => { Name => 'Copyright', Groups => { 2 => 'Author' } },
6423             creationdate=> {
6424             Name => 'CreationDate',
6425             Groups => { 2 => 'Time' },
6426             Shift => 'Time',
6427             ValueConv => q{
6428             require Image::ExifTool::XMP;
6429             $val = Image::ExifTool::XMP::ConvertXMPDate($val,1);
6430             $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
6431             return $val;
6432             },
6433             ValueConvInv => q{
6434             require Image::ExifTool::XMP;
6435             $val = Image::ExifTool::XMP::FormatXMPDate($val);
6436             $val =~ s/([-+]\d{2}):(\d{2})$/$1$2/; # remove time zone colon
6437             return $val;
6438             },
6439             PrintConv => '$self->ConvertDateTime($val)',
6440             PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
6441             },
6442             description => { },
6443             director => { },
6444             displayname => { Name => 'DisplayName' },
6445             title => { }, #22
6446             genre => { },
6447             information => { },
6448             keywords => { },
6449             producer => { }, #22
6450             make => { Name => 'Make', Groups => { 2 => 'Camera' } },
6451             model => { Name => 'Model', Groups => { 2 => 'Camera' } },
6452             publisher => { },
6453             software => { },
6454             year => { Groups => { 2 => 'Time' } },
6455             'camera.identifier' => 'CameraIdentifier', # (iPhone 4)
6456             'camera.framereadouttimeinmicroseconds' => { # (iPhone 4)
6457             Name => 'FrameReadoutTime',
6458             ValueConv => '$val * 1e-6',
6459             ValueConvInv => 'int($val * 1e6 + 0.5)',
6460             PrintConv => '$val * 1e6 . " microseconds"',
6461             PrintConvInv => '$val =~ s/ .*//; $val * 1e-6',
6462             },
6463             'location.ISO6709' => {
6464             Name => 'GPSCoordinates',
6465             Groups => { 2 => 'Location' },
6466             Notes => q{
6467             Google Photos may ignore this if the coorinates have more than 5 digits
6468             after the decimal
6469             },
6470             ValueConv => \&ConvertISO6709,
6471             ValueConvInv => \&ConvInvISO6709,
6472             PrintConv => \&PrintGPSCoordinates,
6473             PrintConvInv => \&PrintInvGPSCoordinates,
6474             },
6475             'location.name' => { Name => 'LocationName', Groups => { 2 => 'Location' } },
6476             'location.body' => { Name => 'LocationBody', Groups => { 2 => 'Location' } },
6477             'location.note' => { Name => 'LocationNote', Groups => { 2 => 'Location' } },
6478             'location.role' => {
6479             Name => 'LocationRole',
6480             Groups => { 2 => 'Location' },
6481             PrintConv => {
6482             0 => 'Shooting Location',
6483             1 => 'Real Location',
6484             2 => 'Fictional Location',
6485             },
6486             },
6487             'location.date' => {
6488             Name => 'LocationDate',
6489             Groups => { 2 => 'Time' },
6490             Shift => 'Time',
6491             ValueConv => q{
6492             require Image::ExifTool::XMP;
6493             $val = Image::ExifTool::XMP::ConvertXMPDate($val);
6494             $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
6495             return $val;
6496             },
6497             ValueConvInv => q{
6498             require Image::ExifTool::XMP;
6499             $val = Image::ExifTool::XMP::FormatXMPDate($val);
6500             $val =~ s/([-+]\d{2}):(\d{2})$/$1$2/; # remove time zone colon
6501             return $val;
6502             },
6503             PrintConv => '$self->ConvertDateTime($val)',
6504             PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
6505             },
6506             'location.accuracy.horizontal' => { Name => 'LocationAccuracyHorizontal' },
6507             'live-photo.auto' => { Name => 'LivePhotoAuto', Writable => 'int8u' },
6508             'live-photo.vitality-score' => { Name => 'LivePhotoVitalityScore', Writable => 'float' },
6509             'live-photo.vitality-scoring-version' => { Name => 'LivePhotoVitalityScoringVersion', Writable => 'int64s' },
6510             'apple.photos.variation-identifier' => { Name => 'ApplePhotosVariationIdentifier', Writable => 'int64s' },
6511             'direction.facing' => { Name => 'CameraDirection', Groups => { 2 => 'Location' } },
6512             'direction.motion' => { Name => 'CameraMotion', Groups => { 2 => 'Location' } },
6513             'location.body' => { Name => 'LocationBody', Groups => { 2 => 'Location' } },
6514             'player.version' => 'PlayerVersion',
6515             'player.movie.visual.brightness'=> 'Brightness',
6516             'player.movie.visual.color' => 'Color',
6517             'player.movie.visual.tint' => 'Tint',
6518             'player.movie.visual.contrast' => 'Contrast',
6519             'player.movie.audio.gain' => 'AudioGain',
6520             'player.movie.audio.treble' => 'Treble',
6521             'player.movie.audio.bass' => 'Bass',
6522             'player.movie.audio.balance' => 'Balance',
6523             'player.movie.audio.pitchshift' => 'PitchShift',
6524             'player.movie.audio.mute' => {
6525             Name => 'Mute',
6526             Format => 'int8u',
6527             PrintConv => { 0 => 'Off', 1 => 'On' },
6528             },
6529             'rating.user' => 'UserRating', # (Canon ELPH 510 HS)
6530             'collection.user' => 'UserCollection', #22
6531             'Encoded_With' => 'EncodedWith',
6532             #
6533             # the following tags aren't in the com.apple.quicktime namespace:
6534             #
6535             'com.apple.photos.captureMode' => 'CaptureMode',
6536             'com.android.version' => 'AndroidVersion',
6537             #
6538             # also seen
6539             #
6540             # com.divergentmedia.clipwrap.model ('NEX-FS700EK')
6541             # com.divergentmedia.clipwrap.model1 ('49')
6542             # com.divergentmedia.clipwrap.model2 ('0')
6543             # com.divergentmedia.clipwrap.manufacturer ('Sony')
6544             # com.divergentmedia.clipwrap.originalDateTime ('2013/2/6 10:30:40+0200')
6545             #
6546             # seen in timed metadata (mebx), and added dynamically to the table via SaveMetaKeys()
6547             # NOTE: these tags are not writable! (timed metadata cannot yet be written)
6548             #
6549             # (mdta)com.apple.quicktime.video-orientation (dtyp=66, int16s)
6550             'video-orientation' => {
6551             Name => 'VideoOrientation',
6552             Writable => 0,
6553             PrintConv => \%Image::ExifTool::Exif::orientation, #PH (NC)
6554             },
6555             # (mdta)com.apple.quicktime.live-photo-info (dtyp=com.apple.quicktime.com.apple.quicktime.live-photo-info)
6556             'live-photo-info' => {
6557             Name => 'LivePhotoInfo',
6558             Writable => 0,
6559             # not sure what these values mean, but unpack them anyway - PH
6560             # (ignore the fact that the "f" and "l" unpacks won't work on a big-endian machine)
6561             ValueConv => 'join " ",unpack "VfVVf6c4lCCcclf4Vvv", $val',
6562             },
6563             # (mdta)com.apple.quicktime.still-image-time (dtyp=65, int8s)
6564             'still-image-time' => { # (found in live photo)
6565             Name => 'StillImageTime',
6566             Writable => 0,
6567             Notes => q{
6568             this tag always has a value of -1; the time of the still image is obtained
6569             from the associated SampleTime
6570             },
6571             },
6572             # (mdta)com.apple.quicktime.detected-face (dtyp='com.apple.quicktime.detected-face')
6573             'detected-face' => {
6574             Name => 'FaceInfo',
6575             Writable => 0,
6576             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::FaceInfo' },
6577             },
6578             # ---- detected-face fields ( ----
6579             # --> back here after a round trip through FaceInfo -> FaceRec -> FaceItem
6580             # (fiel)com.apple.quicktime.detected-face.bounds (dtyp=80, float[8])
6581             'detected-face.bounds' => {
6582             Name => 'DetectedFaceBounds',
6583             Writable => 0,
6584             # round to a reasonable number of decimal places
6585             PrintConv => 'my @a=split " ",$val;$_=int($_*1e6+.5)/1e6 foreach @a;join " ",@a',
6586             PrintConvInv => '$val',
6587             },
6588             # (fiel)com.apple.quicktime.detected-face.face-id (dtyp=77, int32u)
6589             'detected-face.face-id' => { Name => 'DetectedFaceID', Writable => 0 },
6590             # (fiel)com.apple.quicktime.detected-face.roll-angle (dtyp=23, float)
6591             'detected-face.roll-angle' => { Name => 'DetectedFaceRollAngle', Writable => 0 },
6592             # (fiel)com.apple.quicktime.detected-face.yaw-angle (dtyp=23, float)
6593             'detected-face.yaw-angle' => { Name => 'DetectedFaceYawAngle', Writable => 0 },
6594             #
6595             # seen in Apple ProRes RAW file
6596             #
6597             # (mdta)com.apple.proapps.manufacturer (eg. "Sony")
6598             # (mdta)com.apple.proapps.exif.{Exif}.FNumber (float, eg. 1.0)
6599             # (mdta)org.smpte.rdd18.lens.irisfnumber (eg. "F1.0")
6600             # (mdta)com.apple.proapps.exif.{Exif}.ShutterSpeedValue (float, eg. 1.006)
6601             # (mdta)org.smpte.rdd18.camera.shutterspeed_angle (eg. "179.2deg")
6602             # (mdta)org.smpte.rdd18.camera.neutraldensityfilterwheelsetting (eg. "ND1")
6603             # (mdta)org.smpte.rdd18.camera.whitebalance (eg. "4300K")
6604             # (mdta)com.apple.proapps.exif.{Exif}.ExposureIndex (float, eg. 4000)
6605             # (mdta)org.smpte.rdd18.camera.isosensitivity (eg. "4000")
6606             # (mdta)com.apple.proapps.image.{TIFF}.Make (eg. "Atmos")
6607             # (mdta)com.apple.proapps.image.{TIFF}.Model (eg. "ShogunInferno")
6608             # (mdta)com.apple.proapps.image.{TIFF}.Software (eg. "9.0")
6609             );
6610              
6611             # iTunes info ('----') atoms
6612             %Image::ExifTool::QuickTime::iTunesInfo = (
6613             PROCESS_PROC => \&ProcessMOV,
6614             GROUPS => { 1 => 'iTunes', 2 => 'Audio' },
6615             VARS => { LONG_TAGS => 0 }, # (hack for discrepancy in the way long tags are counted in BuildTagLookup)
6616             NOTES => q{
6617             ExifTool will extract any iTunesInfo tags that exist, even if they are not
6618             defined in this table. These tags belong to the family 1 "iTunes" group,
6619             and are not currently writable.
6620             },
6621             # 'mean'/'name'/'data' atoms form a triplet, but unfortunately
6622             # I haven't been able to find any documentation on this.
6623             # 'mean' is normally 'com.apple.iTunes'
6624             mean => {
6625             Name => 'Mean',
6626             # the 'Triplet' flag tells ProcessMOV() to generate
6627             # a single tag from the mean/name/data triplet
6628             Triplet => 1,
6629             Hidden => 1,
6630             },
6631             name => {
6632             Name => 'Name',
6633             Triplet => 1,
6634             Hidden => 1,
6635             },
6636             data => {
6637             Name => 'Data',
6638             Triplet => 1,
6639             Hidden => 1,
6640             },
6641             # the tag ID's below are composed from "mean/name",
6642             # but "mean/" is omitted if it is "com.apple.iTunes/":
6643             'iTunMOVI' => {
6644             Name => 'iTunMOVI',
6645             SubDirectory => { TagTable => 'Image::ExifTool::PLIST::Main' },
6646             },
6647             'tool' => {
6648             Name => 'iTunTool',
6649             Description => 'iTunTool',
6650             Format => 'int32u',
6651             PrintConv => 'sprintf("0x%.8x",$val)',
6652             },
6653             'iTunEXTC' => {
6654             Name => 'ContentRating',
6655             Notes => 'standard | rating | score | reasons',
6656             # eg. 'us-tv|TV-14|500|V', 'mpaa|PG-13|300|For violence and sexuality'
6657             # (see http://shadowofged.blogspot.ca/2008/06/itunes-content-ratings.html)
6658             },
6659             'iTunNORM' => {
6660             Name => 'VolumeNormalization',
6661             PrintConv => '$val=~s/ 0+(\w)/ $1/g; $val=~s/^\s+//; $val',
6662             },
6663             'iTunSMPB' => {
6664             Name => 'iTunSMPB',
6665             Description => 'iTunSMPB',
6666             # hex format, similar to iTunNORM, but 12 words instead of 10,
6667             # and 4th word is 16 hex digits (all others are 8)
6668             # (gives AAC encoder delay, ref http://code.google.com/p/l-smash/issues/detail?id=1)
6669             PrintConv => '$val=~s/ 0+(\w)/ $1/g; $val=~s/^\s+//; $val',
6670             },
6671             # (CDDB = Compact Disc DataBase)
6672             # iTunes_CDDB_1 = +<# tracks>+...
6673             'iTunes_CDDB_1' => 'CDDB1Info',
6674             'iTunes_CDDB_TrackNumber' => 'CDDBTrackNumber',
6675             'Encoding Params' => {
6676             Name => 'EncodingParams',
6677             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::EncodingParams' },
6678             },
6679             # also heard about 'iTunPGAP', but I haven't seen a sample
6680             # all tags below were added based on samples I have seen - PH
6681             DISCNUMBER => 'DiscNumber',
6682             TRACKNUMBER => 'TrackNumber',
6683             ARTISTS => 'Artists',
6684             CATALOGNUMBER => 'CatalogNumber',
6685             RATING => 'Rating',
6686             MEDIA => 'Media',
6687             SCRIPT => 'Script', # character set? (seen 'Latn')
6688             BARCODE => 'Barcode',
6689             LABEL => 'Label',
6690             MOOD => 'Mood',
6691             popularimeter => 'Popularimeter',
6692             'Dynamic Range (DR)'=> 'DynamicRange',
6693             initialkey => 'InitialKey',
6694             originalyear => 'OriginalYear',
6695             originaldate => 'OriginalDate',
6696             '~length' => 'Length', # play length? (ie. duration?)
6697             replaygain_track_gain=>'ReplayTrackGain',
6698             replaygain_track_peak=>'ReplayTrackPeak',
6699             'Volume Level (ReplayGain)'=> 'ReplayVolumeLevel',
6700             'Dynamic Range (R128)'=> 'DynamicRangeR128',
6701             'Volume Level (R128)' => 'VolumeLevelR128',
6702             'Peak Level (Sample)' => 'PeakLevelSample',
6703             'Peak Level (R128)' => 'PeakLevelR128',
6704             # also seen (many from forum12777):
6705             # 'MusicBrainz Album Release Country'
6706             # 'MusicBrainz Album Type'
6707             # 'MusicBrainz Album Status'
6708             # 'MusicBrainz Track Id'
6709             # 'MusicBrainz Release Track Id'
6710             # 'MusicBrainz Album Id'
6711             # 'MusicBrainz Album Artist Id'
6712             # 'MusicBrainz Artist Id'
6713             # 'Acoustid Id' (sic)
6714             # 'Tool Version'
6715             # 'Tool Name'
6716             # 'ISRC'
6717             # 'HDCD'
6718             # 'Waveform'
6719             );
6720              
6721             # iTunes audio encoding parameters
6722             # ref https://developer.apple.com/library/mac/#documentation/MusicAudio/Reference/AudioCodecServicesRef/Reference/reference.html
6723             %Image::ExifTool::QuickTime::EncodingParams = (
6724             PROCESS_PROC => \&ProcessEncodingParams,
6725             GROUPS => { 2 => 'Audio' },
6726             # (I have commented out the ones that don't have integer values because they
6727             # probably don't appear, and definitely wouldn't work with current decoding - PH)
6728              
6729             # global codec properties
6730             #'lnam' => 'AudioCodecName',
6731             #'lmak' => 'AudioCodecManufacturer',
6732             #'lfor' => 'AudioCodecFormat',
6733             'vpk?' => 'AudioHasVariablePacketByteSizes',
6734             #'ifm#' => 'AudioSupportedInputFormats',
6735             #'ofm#' => 'AudioSupportedOutputFormats',
6736             #'aisr' => 'AudioAvailableInputSampleRates',
6737             #'aosr' => 'AudioAvailableOutputSampleRates',
6738             'abrt' => 'AudioAvailableBitRateRange',
6739             'mnip' => 'AudioMinimumNumberInputPackets',
6740             'mnop' => 'AudioMinimumNumberOutputPackets',
6741             'cmnc' => 'AudioAvailableNumberChannels',
6742             'lmrc' => 'AudioDoesSampleRateConversion',
6743             #'aicl' => 'AudioAvailableInputChannelLayoutTags',
6744             #'aocl' => 'AudioAvailableOutputChannelLayoutTags',
6745             #'if4o' => 'AudioInputFormatsForOutputFormat',
6746             #'of4i' => 'AudioOutputFormatsForInputFormat',
6747             #'acfi' => 'AudioFormatInfo',
6748              
6749             # instance codec properties
6750             'tbuf' => 'AudioInputBufferSize',
6751             'pakf' => 'AudioPacketFrameSize',
6752             'pakb' => 'AudioMaximumPacketByteSize',
6753             #'ifmt' => 'AudioCurrentInputFormat',
6754             #'ofmt' => 'AudioCurrentOutputFormat',
6755             #'kuki' => 'AudioMagicCookie',
6756             'ubuf' => 'AudioUsedInputBufferSize',
6757             'init' => 'AudioIsInitialized',
6758             'brat' => 'AudioCurrentTargetBitRate',
6759             #'cisr' => 'AudioCurrentInputSampleRate',
6760             #'cosr' => 'AudioCurrentOutputSampleRate',
6761             'srcq' => 'AudioQualitySetting',
6762             #'brta' => 'AudioApplicableBitRateRange',
6763             #'isra' => 'AudioApplicableInputSampleRates',
6764             #'osra' => 'AudioApplicableOutputSampleRates',
6765             'pad0' => 'AudioZeroFramesPadded',
6766             'prmm' => 'AudioCodecPrimeMethod',
6767             #'prim' => 'AudioCodecPrimeInfo',
6768             #'icl ' => 'AudioInputChannelLayout',
6769             #'ocl ' => 'AudioOutputChannelLayout',
6770             #'acs ' => 'AudioCodecSettings',
6771             #'acfl' => 'AudioCodecFormatList',
6772             'acbf' => 'AudioBitRateControlMode',
6773             'vbrq' => 'AudioVBRQuality',
6774             'mdel' => 'AudioMinimumDelayMode',
6775              
6776             # deprecated
6777             'pakd' => 'AudioRequiresPacketDescription',
6778             #'brt#' => 'AudioAvailableBitRates',
6779             'acef' => 'AudioExtendFrequencies',
6780             'ursr' => 'AudioUseRecommendedSampleRate',
6781             'oppr' => 'AudioOutputPrecedence',
6782             #'loud' => 'AudioCurrentLoudnessStatistics',
6783              
6784             # others
6785             'vers' => 'AudioEncodingParamsVersion', #PH
6786             'cdcv' => { #PH
6787             Name => 'AudioComponentVersion',
6788             ValueConv => 'join ".", unpack("ncc", pack("N",$val))',
6789             },
6790             );
6791              
6792             # print to video data block
6793             %Image::ExifTool::QuickTime::Video = (
6794             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
6795             GROUPS => { 2 => 'Video' },
6796             0 => {
6797             Name => 'DisplaySize',
6798             PrintConv => {
6799             0 => 'Normal',
6800             1 => 'Double Size',
6801             2 => 'Half Size',
6802             3 => 'Full Screen',
6803             4 => 'Current Size',
6804             },
6805             },
6806             6 => {
6807             Name => 'SlideShow',
6808             PrintConv => {
6809             0 => 'No',
6810             1 => 'Yes',
6811             },
6812             },
6813             );
6814              
6815             # 'hnti' atoms
6816             %Image::ExifTool::QuickTime::HintInfo = (
6817             PROCESS_PROC => \&ProcessMOV,
6818             GROUPS => { 2 => 'Video' },
6819             'rtp ' => {
6820             Name => 'RealtimeStreamingProtocol',
6821             PrintConv => '$val=~s/^sdp /(SDP) /; $val',
6822             },
6823             'sdp ' => 'StreamingDataProtocol',
6824             );
6825              
6826             # 'hinf' atoms
6827             %Image::ExifTool::QuickTime::HintTrackInfo = (
6828             PROCESS_PROC => \&ProcessMOV,
6829             GROUPS => { 2 => 'Video' },
6830             trpY => { Name => 'TotalBytes', Format => 'int64u' }, #(documented)
6831             trpy => { Name => 'TotalBytes', Format => 'int64u' }, #(observed)
6832             totl => { Name => 'TotalBytes', Format => 'int32u' },
6833             nump => { Name => 'NumPackets', Format => 'int64u' },
6834             npck => { Name => 'NumPackets', Format => 'int32u' },
6835             tpyl => { Name => 'TotalBytesNoRTPHeaders', Format => 'int64u' },
6836             tpaY => { Name => 'TotalBytesNoRTPHeaders', Format => 'int32u' }, #(documented)
6837             tpay => { Name => 'TotalBytesNoRTPHeaders', Format => 'int32u' }, #(observed)
6838             maxr => {
6839             Name => 'MaxDataRate',
6840             Format => 'int32u',
6841             Count => 2,
6842             PrintConv => 'my @a=split(" ",$val);sprintf("%d bytes in %.3f s",$a[1],$a[0]/1000)',
6843             },
6844             dmed => { Name => 'MediaTrackBytes', Format => 'int64u' },
6845             dimm => { Name => 'ImmediateDataBytes', Format => 'int64u' },
6846             drep => { Name => 'RepeatedDataBytes', Format => 'int64u' },
6847             tmin => {
6848             Name => 'MinTransmissionTime',
6849             Format => 'int32u',
6850             PrintConv => 'sprintf("%.3f s",$val/1000)',
6851             },
6852             tmax => {
6853             Name => 'MaxTransmissionTime',
6854             Format => 'int32u',
6855             PrintConv => 'sprintf("%.3f s",$val/1000)',
6856             },
6857             pmax => { Name => 'LargestPacketSize', Format => 'int32u' },
6858             dmax => {
6859             Name => 'LargestPacketDuration',
6860             Format => 'int32u',
6861             PrintConv => 'sprintf("%.3f s",$val/1000)',
6862             },
6863             payt => {
6864             Name => 'PayloadType',
6865             Format => 'undef', # (necessary to prevent decoding as string!)
6866             ValueConv => 'unpack("N",$val) . " " . substr($val, 5)',
6867             PrintConv => '$val=~s/ /, /;$val',
6868             },
6869             );
6870              
6871             # MP4 media box (ref 5)
6872             %Image::ExifTool::QuickTime::Media = (
6873             PROCESS_PROC => \&ProcessMOV,
6874             WRITE_PROC => \&WriteQuickTime,
6875             GROUPS => { 1 => 'Track#', 2 => 'Video' },
6876             NOTES => 'MP4 media box.',
6877             mdhd => {
6878             Name => 'MediaHeader',
6879             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MediaHeader' },
6880             },
6881             hdlr => {
6882             Name => 'Handler',
6883             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Handler' },
6884             },
6885             minf => {
6886             Name => 'MediaInfo',
6887             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MediaInfo' },
6888             },
6889             );
6890              
6891             # MP4 media header box (ref 5)
6892             %Image::ExifTool::QuickTime::MediaHeader = (
6893             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
6894             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
6895             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
6896             GROUPS => { 1 => 'Track#', 2 => 'Video' },
6897             FORMAT => 'int32u',
6898             DATAMEMBER => [ 0, 1, 2, 3, 4 ],
6899             0 => {
6900             Name => 'MediaHeaderVersion',
6901             RawConv => '$$self{MediaHeaderVersion} = $val',
6902             },
6903             1 => {
6904             Name => 'MediaCreateDate',
6905             Groups => { 2 => 'Time' },
6906             %timeInfo,
6907             # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
6908             Hook => '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
6909             },
6910             2 => {
6911             Name => 'MediaModifyDate',
6912             Groups => { 2 => 'Time' },
6913             %timeInfo,
6914             # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
6915             Hook => '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
6916             },
6917             3 => {
6918             Name => 'MediaTimeScale',
6919             RawConv => '$$self{MediaTS} = $val',
6920             },
6921             4 => {
6922             Name => 'MediaDuration',
6923             RawConv => '$$self{MediaTS} ? $val / $$self{MediaTS} : $val',
6924             PrintConv => '$$self{MediaTS} ? ConvertDuration($val) : $val',
6925             # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
6926             Hook => '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
6927             },
6928             5 => {
6929             Name => 'MediaLanguageCode',
6930             Format => 'int16u',
6931             RawConv => '$val ? $val : undef',
6932             # allow both Macintosh (for MOV files) and ISO (for MP4 files) language codes
6933             ValueConv => '($val < 0x400 or $val == 0x7fff) ? $val : pack "C*", map { (($val>>$_)&0x1f)+0x60 } 10, 5, 0',
6934             PrintConv => q{
6935             return $val unless $val =~ /^\d+$/;
6936             require Image::ExifTool::Font;
6937             return $Image::ExifTool::Font::ttLang{Macintosh}{$val} || "Unknown ($val)";
6938             },
6939             },
6940             );
6941              
6942             # MP4 media information box (ref 5)
6943             %Image::ExifTool::QuickTime::MediaInfo = (
6944             PROCESS_PROC => \&ProcessMOV,
6945             WRITE_PROC => \&WriteQuickTime,
6946             GROUPS => { 1 => 'Track#', 2 => 'Video' },
6947             NOTES => 'MP4 media info box.',
6948             vmhd => {
6949             Name => 'VideoHeader',
6950             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::VideoHeader' },
6951             },
6952             smhd => {
6953             Name => 'AudioHeader',
6954             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AudioHeader' },
6955             },
6956             hmhd => {
6957             Name => 'HintHeader',
6958             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HintHeader' },
6959             },
6960             nmhd => {
6961             Name => 'NullMediaHeader',
6962             Flags => ['Binary','Unknown'],
6963             },
6964             dinf => {
6965             Name => 'DataInfo', # (don't change this name -- used to recognize directory when writing)
6966             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::DataInfo' },
6967             },
6968             gmhd => {
6969             Name => 'GenMediaHeader',
6970             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::GenMediaHeader' },
6971             },
6972             hdlr => { #PH
6973             Name => 'Handler',
6974             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Handler' },
6975             },
6976             stbl => {
6977             Name => 'SampleTable',
6978             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SampleTable' },
6979             },
6980             );
6981              
6982             # MP4 video media header (ref 5)
6983             %Image::ExifTool::QuickTime::VideoHeader = (
6984             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
6985             GROUPS => { 2 => 'Video' },
6986             NOTES => 'MP4 video media header.',
6987             FORMAT => 'int16u',
6988             2 => {
6989             Name => 'GraphicsMode',
6990             PrintHex => 1,
6991             SeparateTable => 'GraphicsMode',
6992             PrintConv => \%graphicsMode,
6993             },
6994             3 => { Name => 'OpColor', Format => 'int16u[3]' },
6995             );
6996              
6997             # MP4 audio media header (ref 5)
6998             %Image::ExifTool::QuickTime::AudioHeader = (
6999             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7000             GROUPS => { 2 => 'Audio' },
7001             NOTES => 'MP4 audio media header.',
7002             FORMAT => 'int16u',
7003             2 => { Name => 'Balance', Format => 'fixed16s' },
7004             );
7005              
7006             # MP4 hint media header (ref 5)
7007             %Image::ExifTool::QuickTime::HintHeader = (
7008             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7009             NOTES => 'MP4 hint media header.',
7010             FORMAT => 'int16u',
7011             2 => 'MaxPDUSize',
7012             3 => 'AvgPDUSize',
7013             4 => { Name => 'MaxBitrate', Format => 'int32u', PrintConv => 'ConvertBitrate($val)' },
7014             6 => { Name => 'AvgBitrate', Format => 'int32u', PrintConv => 'ConvertBitrate($val)' },
7015             );
7016              
7017             # MP4 sample table box (ref 5)
7018             %Image::ExifTool::QuickTime::SampleTable = (
7019             PROCESS_PROC => \&ProcessMOV,
7020             WRITE_PROC => \&WriteQuickTime,
7021             GROUPS => { 2 => 'Video' },
7022             NOTES => 'MP4 sample table box.',
7023             stsd => [
7024             {
7025             Name => 'AudioSampleDesc',
7026             Condition => '$$self{HandlerType} and $$self{HandlerType} eq "soun"',
7027             SubDirectory => {
7028             TagTable => 'Image::ExifTool::QuickTime::AudioSampleDesc',
7029             ProcessProc => \&ProcessSampleDesc,
7030             },
7031             },{
7032             Name => 'VideoSampleDesc',
7033             Condition => '$$self{HandlerType} and $$self{HandlerType} eq "vide"',
7034             SubDirectory => {
7035             TagTable => 'Image::ExifTool::QuickTime::ImageDesc',
7036             ProcessProc => \&ProcessSampleDesc,
7037             },
7038             },{
7039             Name => 'HintSampleDesc',
7040             Condition => '$$self{HandlerType} and $$self{HandlerType} eq "hint"',
7041             SubDirectory => {
7042             TagTable => 'Image::ExifTool::QuickTime::HintSampleDesc',
7043             ProcessProc => \&ProcessSampleDesc,
7044             },
7045             },{
7046             Name => 'MetaSampleDesc',
7047             Condition => '$$self{HandlerType} and $$self{HandlerType} eq "meta"',
7048             SubDirectory => {
7049             TagTable => 'Image::ExifTool::QuickTime::MetaSampleDesc',
7050             ProcessProc => \&ProcessSampleDesc,
7051             },
7052             },{
7053             Name => 'OtherSampleDesc',
7054             SubDirectory => {
7055             TagTable => 'Image::ExifTool::QuickTime::OtherSampleDesc',
7056             ProcessProc => \&ProcessSampleDesc,
7057             },
7058             },
7059             # (Note: "alis" HandlerType handled by the parent audio or video handler)
7060             ],
7061             stts => [ # decoding time-to-sample table
7062             {
7063             Name => 'VideoFrameRate',
7064             Notes => 'average rate calculated from time-to-sample table for video media',
7065             Condition => '$$self{HandlerType} and $$self{HandlerType} eq "vide"',
7066             Format => 'undef', # (necessary to prevent decoding as string!)
7067             # (must be RawConv so appropriate MediaTS is used in calculation)
7068             RawConv => 'Image::ExifTool::QuickTime::CalcSampleRate($self, \$val)',
7069             PrintConv => 'int($val * 1000 + 0.5) / 1000',
7070             },
7071             {
7072             Name => 'TimeToSampleTable',
7073             Flags => ['Binary','Unknown'],
7074             },
7075             ],
7076             ctts => {
7077             Name => 'CompositionTimeToSample',
7078             Flags => ['Binary','Unknown'],
7079             },
7080             stsc => {
7081             Name => 'SampleToChunk',
7082             Flags => ['Binary','Unknown'],
7083             },
7084             stsz => {
7085             Name => 'SampleSizes',
7086             Flags => ['Binary','Unknown'],
7087             },
7088             stz2 => {
7089             Name => 'CompactSampleSizes',
7090             Flags => ['Binary','Unknown'],
7091             },
7092             stco => {
7093             Name => 'ChunkOffset',
7094             Flags => ['Binary','Unknown'],
7095             },
7096             co64 => {
7097             Name => 'ChunkOffset64',
7098             Flags => ['Binary','Unknown'],
7099             },
7100             stss => {
7101             Name => 'SyncSampleTable',
7102             Flags => ['Binary','Unknown'],
7103             },
7104             stsh => {
7105             Name => 'ShadowSyncSampleTable',
7106             Flags => ['Binary','Unknown'],
7107             },
7108             padb => {
7109             Name => 'SamplePaddingBits',
7110             Flags => ['Binary','Unknown'],
7111             },
7112             stdp => {
7113             Name => 'SampleDegradationPriority',
7114             Flags => ['Binary','Unknown'],
7115             },
7116             sdtp => {
7117             Name => 'IdependentAndDisposableSamples',
7118             Flags => ['Binary','Unknown'],
7119             },
7120             sbgp => {
7121             Name => 'SampleToGroup',
7122             Flags => ['Binary','Unknown'],
7123             },
7124             sgpd => {
7125             Name => 'SampleGroupDescription',
7126             Flags => ['Binary','Unknown'],
7127             # bytes 4-7 give grouping type (ref ISO/IEC 14496-15:2014)
7128             # tsas - temporal sublayer sample
7129             # stsa - step-wise temporal layer access
7130             # avss - AVC sample
7131             # tscl - temporal layer scalability
7132             # sync - sync sample
7133             },
7134             subs => {
7135             Name => 'Sub-sampleInformation',
7136             Flags => ['Binary','Unknown'],
7137             },
7138             cslg => {
7139             Name => 'CompositionToDecodeTimelineMapping',
7140             Flags => ['Binary','Unknown'],
7141             },
7142             stps => {
7143             Name => 'PartialSyncSamples',
7144             ValueConv => 'join " ",unpack("x8N*",$val)',
7145             },
7146             # mark - 8 bytes all zero (GoPro)
7147             );
7148              
7149             # MP4 audio sample description box (ref 5/AtomicParsley 0.9.4 parsley.cpp)
7150             %Image::ExifTool::QuickTime::AudioSampleDesc = (
7151             PROCESS_PROC => \&ProcessHybrid,
7152             VARS => { ID_LABEL => 'ID/Index' },
7153             GROUPS => { 2 => 'Audio' },
7154             NOTES => q{
7155             MP4 audio sample description. This hybrid atom contains both data and child
7156             atoms.
7157             },
7158             4 => {
7159             Name => 'AudioFormat',
7160             Format => 'undef[4]',
7161             RawConv => q{
7162             $$self{AudioFormat} = $val;
7163             return undef unless $val =~ /^[\w ]{4}$/i;
7164             # check for protected audio format
7165             $self->OverrideFileType('M4P') if $val eq 'drms' and $$self{FileType} eq 'M4A';
7166             return $val;
7167             },
7168             # see this link for print conversions (not complete):
7169             # https://github.com/yannickcr/brooser/blob/master/php/librairies/getid3/module.audio-video.quicktime.php
7170             },
7171             20 => { #PH
7172             Name => 'AudioVendorID',
7173             Condition => '$$self{AudioFormat} ne "mp4s"',
7174             Format => 'undef[4]',
7175             RawConv => '$val eq "\0\0\0\0" ? undef : $val',
7176             PrintConv => \%vendorID,
7177             SeparateTable => 'VendorID',
7178             },
7179             24 => { Name => 'AudioChannels', Format => 'int16u' },
7180             26 => { Name => 'AudioBitsPerSample', Format => 'int16u' },
7181             32 => { Name => 'AudioSampleRate', Format => 'fixed32u' },
7182             #
7183             # Observed offsets for child atoms of various AudioFormat types:
7184             #
7185             # AudioFormat Offset Child atoms
7186             # ----------- ------ ----------------
7187             # mp4a 52 * wave, chan, esds, SA3D(Insta360 spherical video params?,also GoPro Max)
7188             # in24 52 wave, chan
7189             # "ms\0\x11" 52 wave
7190             # sowt 52 chan
7191             # mp4a 36 * esds, pinf
7192             # drms 36 esds, sinf
7193             # samr 36 damr
7194             # alac 36 alac
7195             # ac-3 36 dac3
7196             #
7197             # (* child atoms found at different offsets in mp4a)
7198             #
7199             pinf => {
7200             Name => 'PurchaseInfo',
7201             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ProtectionInfo' },
7202             },
7203             sinf => { # "protection scheme information"
7204             Name => 'ProtectionInfo', #3
7205             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ProtectionInfo' },
7206             },
7207             # f - 16/36 bytes
7208             # esds - 31/40/42/43 bytes - ES descriptor (ref 3)
7209             damr => { #3
7210             Name => 'DecodeConfig',
7211             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::DecodeConfig' },
7212             },
7213             wave => {
7214             Name => 'Wave',
7215             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Wave' },
7216             },
7217             chan => {
7218             Name => 'AudioChannelLayout',
7219             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ChannelLayout' },
7220             }
7221             # alac - 28 bytes
7222             # adrm - AAX DRM atom? 148 bytes
7223             # aabd - AAX unknown 17kB (contains 'aavd' strings)
7224             # SA3D - written by Garmin VIRB360
7225             );
7226              
7227             # AMR decode config box (ref 3)
7228             %Image::ExifTool::QuickTime::DecodeConfig = (
7229             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7230             GROUPS => { 2 => 'Audio' },
7231             0 => {
7232             Name => 'EncoderVendor',
7233             Format => 'undef[4]',
7234             },
7235             4 => 'EncoderVersion',
7236             # 5 - int16u - packet modes
7237             # 7 - int8u - number of packet mode changes
7238             # 8 - int8u - bytes per packet
7239             );
7240              
7241             %Image::ExifTool::QuickTime::ProtectionInfo = (
7242             PROCESS_PROC => \&ProcessMOV,
7243             GROUPS => { 2 => 'Audio' },
7244             NOTES => 'Child atoms found in "sinf" and/or "pinf" atoms.',
7245             frma => 'OriginalFormat',
7246             # imif - IPMP information
7247             schm => {
7248             Name => 'SchemeType',
7249             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SchemeType' },
7250             },
7251             schi => {
7252             Name => 'SchemeInfo',
7253             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SchemeInfo' },
7254             },
7255             enda => {
7256             Name => 'Endianness',
7257             Format => 'int16u',
7258             PrintConv => {
7259             0 => 'Big-endian (Motorola, MM)',
7260             1 => 'Little-endian (Intel, II)',
7261             },
7262             },
7263             # skcr
7264             );
7265              
7266             %Image::ExifTool::QuickTime::Wave = (
7267             PROCESS_PROC => \&ProcessMOV,
7268             frma => 'PurchaseFileFormat',
7269             enda => {
7270             Name => 'Endianness',
7271             Format => 'int16u',
7272             PrintConv => {
7273             0 => 'Big-endian (Motorola, MM)',
7274             1 => 'Little-endian (Intel, II)',
7275             },
7276             },
7277             # "ms\0\x11" - 20 bytes
7278             );
7279              
7280             # audio channel layout (ref CoreAudioTypes.h)
7281             %Image::ExifTool::QuickTime::ChannelLayout = (
7282             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7283             GROUPS => { 2 => 'Audio' },
7284             DATAMEMBER => [ 0, 8 ],
7285             NOTES => 'Audio channel layout.',
7286             # 0 - version and flags
7287             4 => {
7288             Name => 'LayoutFlags',
7289             Format => 'int16u',
7290             RawConv => '$$self{LayoutFlags} = $val',
7291             PrintConvColumns => 2,
7292             PrintConv => {
7293             0 => 'UseDescriptions',
7294             1 => 'UseBitmap',
7295             100 => 'Mono',
7296             101 => 'Stereo',
7297             102 => 'StereoHeadphones',
7298             100 => 'Mono',
7299             101 => 'Stereo',
7300             102 => 'StereoHeadphones',
7301             103 => 'MatrixStereo',
7302             104 => 'MidSide',
7303             105 => 'XY',
7304             106 => 'Binaural',
7305             107 => 'Ambisonic_B_Format',
7306             108 => 'Quadraphonic',
7307             109 => 'Pentagonal',
7308             110 => 'Hexagonal',
7309             111 => 'Octagonal',
7310             112 => 'Cube',
7311             113 => 'MPEG_3_0_A',
7312             114 => 'MPEG_3_0_B',
7313             115 => 'MPEG_4_0_A',
7314             116 => 'MPEG_4_0_B',
7315             117 => 'MPEG_5_0_A',
7316             118 => 'MPEG_5_0_B',
7317             119 => 'MPEG_5_0_C',
7318             120 => 'MPEG_5_0_D',
7319             121 => 'MPEG_5_1_A',
7320             122 => 'MPEG_5_1_B',
7321             123 => 'MPEG_5_1_C',
7322             124 => 'MPEG_5_1_D',
7323             125 => 'MPEG_6_1_A',
7324             126 => 'MPEG_7_1_A',
7325             127 => 'MPEG_7_1_B',
7326             128 => 'MPEG_7_1_C',
7327             129 => 'Emagic_Default_7_1',
7328             130 => 'SMPTE_DTV',
7329             131 => 'ITU_2_1',
7330             132 => 'ITU_2_2',
7331             133 => 'DVD_4',
7332             134 => 'DVD_5',
7333             135 => 'DVD_6',
7334             136 => 'DVD_10',
7335             137 => 'DVD_11',
7336             138 => 'DVD_18',
7337             139 => 'AudioUnit_6_0',
7338             140 => 'AudioUnit_7_0',
7339             141 => 'AAC_6_0',
7340             142 => 'AAC_6_1',
7341             143 => 'AAC_7_0',
7342             144 => 'AAC_Octagonal',
7343             145 => 'TMH_10_2_std',
7344             146 => 'TMH_10_2_full',
7345             147 => 'DiscreteInOrder',
7346             148 => 'AudioUnit_7_0_Front',
7347             149 => 'AC3_1_0_1',
7348             150 => 'AC3_3_0',
7349             151 => 'AC3_3_1',
7350             152 => 'AC3_3_0_1',
7351             153 => 'AC3_2_1_1',
7352             154 => 'AC3_3_1_1',
7353             155 => 'EAC_6_0_A',
7354             156 => 'EAC_7_0_A',
7355             157 => 'EAC3_6_1_A',
7356             158 => 'EAC3_6_1_B',
7357             159 => 'EAC3_6_1_C',
7358             160 => 'EAC3_7_1_A',
7359             161 => 'EAC3_7_1_B',
7360             162 => 'EAC3_7_1_C',
7361             163 => 'EAC3_7_1_D',
7362             164 => 'EAC3_7_1_E',
7363             165 => 'EAC3_7_1_F',
7364             166 => 'EAC3_7_1_G',
7365             167 => 'EAC3_7_1_H',
7366             168 => 'DTS_3_1',
7367             169 => 'DTS_4_1',
7368             170 => 'DTS_6_0_A',
7369             171 => 'DTS_6_0_B',
7370             172 => 'DTS_6_0_C',
7371             173 => 'DTS_6_1_A',
7372             174 => 'DTS_6_1_B',
7373             175 => 'DTS_6_1_C',
7374             176 => 'DTS_7_0',
7375             177 => 'DTS_7_1',
7376             178 => 'DTS_8_0_A',
7377             179 => 'DTS_8_0_B',
7378             180 => 'DTS_8_1_A',
7379             181 => 'DTS_8_1_B',
7380             182 => 'DTS_6_1_D',
7381             183 => 'AAC_7_1_B',
7382             0xffff => 'Unknown',
7383             },
7384             },
7385             6 => {
7386             Name => 'AudioChannels',
7387             Condition => '$$self{LayoutFlags} != 0 and $$self{LayoutFlags} != 1',
7388             Format => 'int16u',
7389             },
7390             8 => {
7391             Name => 'AudioChannelTypes',
7392             Condition => '$$self{LayoutFlags} == 1',
7393             Format => 'int32u',
7394             PrintConv => { BITMASK => {
7395             0 => 'Left',
7396             1 => 'Right',
7397             2 => 'Center',
7398             3 => 'LFEScreen',
7399             4 => 'LeftSurround',
7400             5 => 'RightSurround',
7401             6 => 'LeftCenter',
7402             7 => 'RightCenter',
7403             8 => 'CenterSurround',
7404             9 => 'LeftSurroundDirect',
7405             10 => 'RightSurroundDirect',
7406             11 => 'TopCenterSurround',
7407             12 => 'VerticalHeightLeft',
7408             13 => 'VerticalHeightCenter',
7409             14 => 'VerticalHeightRight',
7410             15 => 'TopBackLeft',
7411             16 => 'TopBackCenter',
7412             17 => 'TopBackRight',
7413             }},
7414             },
7415             12 => {
7416             Name => 'NumChannelDescriptions',
7417             Condition => '$$self{LayoutFlags} == 1',
7418             Format => 'int32u',
7419             RawConv => '$$self{NumChannelDescriptions} = $val',
7420             },
7421             16 => {
7422             Name => 'Channel1Label',
7423             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 0',
7424             Format => 'int32u',
7425             SeparateTable => 'ChannelLabel',
7426             PrintConv => \%channelLabel,
7427             },
7428             20 => {
7429             Name => 'Channel1Flags',
7430             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 0',
7431             Format => 'int32u',
7432             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7433             },
7434             24 => {
7435             Name => 'Channel1Coordinates',
7436             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 0',
7437             Notes => q{
7438             3 numbers: for rectangular coordinates left/right, back/front, down/up; for
7439             spherical coordinates left/right degrees, down/up degrees, distance
7440             },
7441             Format => 'float[3]',
7442             },
7443             36 => {
7444             Name => 'Channel2Label',
7445             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 1',
7446             Format => 'int32u',
7447             SeparateTable => 'ChannelLabel',
7448             PrintConv => \%channelLabel,
7449             },
7450             40 => {
7451             Name => 'Channel2Flags',
7452             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 1',
7453             Format => 'int32u',
7454             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7455             },
7456             44 => {
7457             Name => 'Channel2Coordinates',
7458             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 1',
7459             Format => 'float[3]',
7460             },
7461             56 => {
7462             Name => 'Channel3Label',
7463             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 2',
7464             Format => 'int32u',
7465             SeparateTable => 'ChannelLabel',
7466             PrintConv => \%channelLabel,
7467             },
7468             60 => {
7469             Name => 'Channel3Flags',
7470             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 2',
7471             Format => 'int32u',
7472             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7473             },
7474             64 => {
7475             Name => 'Channel3Coordinates',
7476             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 2',
7477             Format => 'float[3]',
7478             },
7479             76 => {
7480             Name => 'Channel4Label',
7481             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 3',
7482             Format => 'int32u',
7483             SeparateTable => 'ChannelLabel',
7484             PrintConv => \%channelLabel,
7485             },
7486             80 => {
7487             Name => 'Channel4Flags',
7488             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 3',
7489             Format => 'int32u',
7490             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7491             },
7492             84 => {
7493             Name => 'Channel4Coordinates',
7494             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 3',
7495             Format => 'float[3]',
7496             },
7497             96 => {
7498             Name => 'Channel5Label',
7499             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 4',
7500             Format => 'int32u',
7501             SeparateTable => 'ChannelLabel',
7502             PrintConv => \%channelLabel,
7503             },
7504             100 => {
7505             Name => 'Channel5Flags',
7506             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 4',
7507             Format => 'int32u',
7508             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7509             },
7510             104 => {
7511             Name => 'Channel5Coordinates',
7512             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 4',
7513             Format => 'float[3]',
7514             },
7515             116 => {
7516             Name => 'Channel6Label',
7517             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 5',
7518             Format => 'int32u',
7519             SeparateTable => 'ChannelLabel',
7520             PrintConv => \%channelLabel,
7521             },
7522             120 => {
7523             Name => 'Channel6Flags',
7524             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 5',
7525             Format => 'int32u',
7526             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7527             },
7528             124 => {
7529             Name => 'Channel6Coordinates',
7530             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 5',
7531             Format => 'float[3]',
7532             },
7533             136 => {
7534             Name => 'Channel7Label',
7535             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 6',
7536             Format => 'int32u',
7537             SeparateTable => 'ChannelLabel',
7538             PrintConv => \%channelLabel,
7539             },
7540             140 => {
7541             Name => 'Channel7Flags',
7542             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 6',
7543             Format => 'int32u',
7544             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7545             },
7546             144 => {
7547             Name => 'Channel7Coordinates',
7548             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 6',
7549             Format => 'float[3]',
7550             },
7551             156 => {
7552             Name => 'Channel8Label',
7553             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 7',
7554             Format => 'int32u',
7555             SeparateTable => 'ChannelLabel',
7556             PrintConv => \%channelLabel,
7557             },
7558             160 => {
7559             Name => 'Channel8Flags',
7560             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 7',
7561             Format => 'int32u',
7562             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7563             },
7564             164 => {
7565             Name => 'Channel8Coordinates',
7566             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 7',
7567             Format => 'float[3]',
7568             },
7569             # (arbitrarily decode only first 8 channels)
7570             );
7571              
7572             # scheme type atom
7573             # ref http://xhelmboyx.tripod.com/formats/mp4-layout.txt
7574             %Image::ExifTool::QuickTime::SchemeType = (
7575             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7576             GROUPS => { 2 => 'Audio' },
7577             # 0 - 4 bytes version
7578             4 => { Name => 'SchemeType', Format => 'undef[4]' },
7579             8 => { Name => 'SchemeVersion', Format => 'int16u' },
7580             10 => { Name => 'SchemeURL', Format => 'string[$size-10]' },
7581             );
7582              
7583             %Image::ExifTool::QuickTime::SchemeInfo = (
7584             PROCESS_PROC => \&ProcessMOV,
7585             GROUPS => { 2 => 'Audio' },
7586             user => {
7587             Name => 'UserID',
7588             Groups => { 2 => 'Author' },
7589             ValueConv => '"0x" . unpack("H*",$val)',
7590             },
7591             cert => { # ref http://www.onvif.org/specs/stream/ONVIF-ExportFileFormat-Spec-v100.pdf
7592             Name => 'Certificate',
7593             ValueConv => '"0x" . unpack("H*",$val)',
7594             },
7595             'key ' => {
7596             Name => 'KeyID',
7597             ValueConv => '"0x" . unpack("H*",$val)',
7598             },
7599             iviv => {
7600             Name => 'InitializationVector',
7601             ValueConv => 'unpack("H*",$val)',
7602             },
7603             righ => {
7604             Name => 'Rights',
7605             Groups => { 2 => 'Author' },
7606             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Rights' },
7607             },
7608             name => { Name => 'UserName', Groups => { 2 => 'Author' } },
7609             # chtb
7610             # priv - private data
7611             # sign
7612             # adkm - Adobe DRM key management system (ref http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf)
7613             # iKMS
7614             # iSFM
7615             # iSLT
7616             );
7617              
7618             %Image::ExifTool::QuickTime::Rights = (
7619             PROCESS_PROC => \&ProcessRights,
7620             GROUPS => { 2 => 'Audio' },
7621             veID => 'ItemVendorID', #PH ("VendorID" ref 19)
7622             plat => 'Platform', #18?
7623             aver => 'VersionRestrictions', #19 ("appversion?" ref 18)
7624             tran => 'TransactionID', #18
7625             song => 'ItemID', #19 ("appid" ref 18)
7626             tool => {
7627             Name => 'ItemTool', #PH (guess) ("itunes build?" ref 18)
7628             Format => 'string',
7629             },
7630             medi => 'MediaFlags', #PH (?)
7631             mode => 'ModeFlags', #PH (?) 0x04 is HD flag (https://compilr.com/heksesang/requiem-mac/UnDrm.java)
7632             );
7633              
7634             # MP4 hint sample description box (ref 5)
7635             # (ref https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-SW1)
7636             %Image::ExifTool::QuickTime::HintSampleDesc = (
7637             PROCESS_PROC => \&ProcessHybrid,
7638             VARS => { ID_LABEL => 'ID/Index' },
7639             NOTES => 'MP4 hint sample description.',
7640             4 => { Name => 'HintFormat', Format => 'undef[4]' },
7641             # 14 - int16u DataReferenceIndex
7642             16 => { Name => 'HintTrackVersion', Format => 'int16u' },
7643             # 18 - int16u LastCompatibleHintTrackVersion
7644             20 => { Name => 'MaxPacketSize', Format => 'int32u' },
7645             #
7646             # Observed offsets for child atoms of various HintFormat types:
7647             #
7648             # HintFormat Offset Child atoms
7649             # ----------- ------ ----------------
7650             # "rtp " 24 tims
7651             #
7652             tims => { Name => 'RTPTimeScale', Format => 'int32u' },
7653             tsro => { Name => 'TimestampRandomOffset', Format => 'int32u' },
7654             snro => { Name => 'SequenceNumberRandomOffset', Format => 'int32u' },
7655             );
7656              
7657             # MP4 metadata sample description box
7658             %Image::ExifTool::QuickTime::MetaSampleDesc = (
7659             PROCESS_PROC => \&ProcessHybrid,
7660             NOTES => 'MP4 metadata sample description.',
7661             4 => {
7662             Name => 'MetaFormat',
7663             Format => 'undef[4]',
7664             RawConv => '$$self{MetaFormat} = $val',
7665             },
7666             8 => { # starts at 8 for MetaFormat eq 'camm', and 17 for 'mett'
7667             Name => 'MetaType',
7668             Format => 'undef[$size-8]',
7669             # may start at various locations!
7670             RawConv => '$$self{MetaType} = ($val=~/(application[^\0]+)/ ? $1 : undef)',
7671             },
7672             #
7673             # Observed offsets for child atoms of various MetaFormat types:
7674             #
7675             # MetaFormat Offset Child atoms
7676             # ----------- ------ ----------------
7677             # mebx 24 keys,btrt,lidp,lidl
7678             # fdsc - -
7679             # gpmd - -
7680             # rtmd - -
7681             # CTMD - -
7682             #
7683             'keys' => { #PH (iPhone7+ hevc)
7684             Name => 'Keys',
7685             SubDirectory => {
7686             TagTable => 'Image::ExifTool::QuickTime::Keys',
7687             ProcessProc => \&ProcessMetaKeys,
7688             },
7689             },
7690             btrt => {
7691             Name => 'BitrateInfo',
7692             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Bitrate' },
7693             },
7694             );
7695              
7696             # MP4 generic sample description box
7697             %Image::ExifTool::QuickTime::OtherSampleDesc = (
7698             PROCESS_PROC => \&ProcessHybrid,
7699             4 => {
7700             Name => 'OtherFormat',
7701             Format => 'undef[4]',
7702             RawConv => '$$self{MetaFormat} = $val', # (yes, use MetaFormat for this too)
7703             },
7704             24 => {
7705             Condition => '$$self{MetaFormat} eq "tmcd"',
7706             Name => 'PlaybackFrameRate', # (may differ from recorded FrameRate eg. ../pics/FujiFilmX-H1.mov)
7707             Format => 'rational64u',
7708             },
7709             #
7710             # Observed offsets for child atoms of various OtherFormat types:
7711             #
7712             # OtherFormat Offset Child atoms
7713             # ----------- ------ ----------------
7714             # avc1 86 avcC
7715             # mp4a 36 esds
7716             # mp4s 16 esds
7717             # tmcd 34 name
7718             # data - -
7719             #
7720             ftab => { Name => 'FontTable', Format => 'undef', ValueConv => 'substr($val, 5)' },
7721             name => { Name => 'OtherName', Format => 'undef', ValueConv => 'substr($val, 4)' },
7722             # mrlh = GM header?
7723             # mrlv = GM data
7724             # mrld = GM data (448-byte records):
7725             # 0 - int32u count
7726             # 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=%
7727             # 8 - int32u ? (0,1,3,4,5)
7728             # 12 - string[64] units
7729             # 76 - int32u ? (1,3,7,15)
7730             # 80 - int32u 0
7731             # 84 - undef[4] ?
7732             # 88 - int16u[6] ?
7733             # 100 - undef[32] ?
7734             # 132 - string[64] measurement name
7735             # 196 - string[64] measurement name
7736             );
7737              
7738             # MP4 data information box (ref 5)
7739             %Image::ExifTool::QuickTime::DataInfo = (
7740             PROCESS_PROC => \&ProcessMOV,
7741             WRITE_PROC => \&WriteQuickTime, # (necessary to parse dref even though we don't change it)
7742             NOTES => 'MP4 data information box.',
7743             dref => {
7744             Name => 'DataRef',
7745             SubDirectory => {
7746             TagTable => 'Image::ExifTool::QuickTime::DataRef',
7747             Start => 8,
7748             },
7749             },
7750             );
7751              
7752             # Generic media header
7753             %Image::ExifTool::QuickTime::GenMediaHeader = (
7754             PROCESS_PROC => \&ProcessMOV,
7755             gmin => {
7756             Name => 'GenMediaInfo',
7757             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::GenMediaInfo' },
7758             },
7759             text => {
7760             Name => 'Text',
7761             Flags => ['Binary','Unknown'],
7762             },
7763             tmcd => {
7764             Name => 'TimeCode',
7765             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TimeCode' },
7766             },
7767             );
7768              
7769             # TimeCode header
7770             %Image::ExifTool::QuickTime::TimeCode = (
7771             PROCESS_PROC => \&ProcessMOV,
7772             tcmi => {
7773             Name => 'TCMediaInfo',
7774             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TCMediaInfo' },
7775             },
7776             );
7777              
7778             # TimeCode media info (ref 12)
7779             %Image::ExifTool::QuickTime::TCMediaInfo = (
7780             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7781             GROUPS => { 2 => 'Video' },
7782             4 => {
7783             Name => 'TextFont',
7784             Format => 'int16u',
7785             PrintConv => { 0 => 'System' },
7786             },
7787             6 => {
7788             Name => 'TextFace',
7789             Format => 'int16u',
7790             PrintConv => {
7791             0 => 'Plain',
7792             BITMASK => {
7793             0 => 'Bold',
7794             1 => 'Italic',
7795             2 => 'Underline',
7796             3 => 'Outline',
7797             4 => 'Shadow',
7798             5 => 'Condense',
7799             6 => 'Extend',
7800             },
7801             },
7802             },
7803             8 => {
7804             Name => 'TextSize',
7805             Format => 'int16u',
7806             },
7807             # 10 - reserved
7808             12 => {
7809             Name => 'TextColor',
7810             Format => 'int16u[3]',
7811             },
7812             18 => {
7813             Name => 'BackgroundColor',
7814             Format => 'int16u[3]',
7815             },
7816             24 => {
7817             Name => 'FontName',
7818             Format => 'pstring',
7819             ValueConv => '$self->Decode($val, $self->Options("CharsetQuickTime"))',
7820             },
7821             );
7822              
7823             # Generic media info (ref http://sourceforge.jp/cvs/view/ntvrec/ntvrec/libqtime/gmin.h?view=co)
7824             %Image::ExifTool::QuickTime::GenMediaInfo = (
7825             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7826             GROUPS => { 2 => 'Video' },
7827             0 => 'GenMediaVersion',
7828             1 => { Name => 'GenFlags', Format => 'int8u[3]' },
7829             4 => { Name => 'GenGraphicsMode',
7830             Format => 'int16u',
7831             PrintHex => 1,
7832             SeparateTable => 'GraphicsMode',
7833             PrintConv => \%graphicsMode,
7834             },
7835             6 => { Name => 'GenOpColor', Format => 'int16u[3]' },
7836             12 => { Name => 'GenBalance', Format => 'fixed16s' },
7837             );
7838              
7839             # MP4 data reference box (ref 5)
7840             %Image::ExifTool::QuickTime::DataRef = (
7841             PROCESS_PROC => \&ProcessMOV,
7842             WRITE_PROC => \&WriteQuickTime, # (necessary to parse dref even though we don't change it)
7843             NOTES => 'MP4 data reference box.',
7844             'url ' => {
7845             Name => 'URL',
7846             Format => 'undef', # (necessary to prevent decoding as string!)
7847             RawConv => q{
7848             # ignore if self-contained (flags bit 0 set)
7849             return undef if unpack("N",$val) & 0x01;
7850             $_ = substr($val,4); s/\0.*//s; $_;
7851             },
7852             },
7853             "url\0" => { # (written by GoPro)
7854             Name => 'URL',
7855             Format => 'undef', # (necessary to prevent decoding as string!)
7856             RawConv => q{
7857             # ignore if self-contained (flags bit 0 set)
7858             return undef if unpack("N",$val) & 0x01;
7859             $_ = substr($val,4); s/\0.*//s; $_;
7860             },
7861             },
7862             'urn ' => {
7863             Name => 'URN',
7864             Format => 'undef', # (necessary to prevent decoding as string!)
7865             RawConv => q{
7866             return undef if unpack("N",$val) & 0x01;
7867             $_ = substr($val,4); s/\0+/; /; s/\0.*//s; $_;
7868             },
7869             },
7870             );
7871              
7872             # MP4 handler box (ref 5)
7873             %Image::ExifTool::QuickTime::Handler = (
7874             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7875             GROUPS => { 2 => 'Video' },
7876             4 => { #PH
7877             Name => 'HandlerClass',
7878             Format => 'undef[4]',
7879             RawConv => '$val eq "\0\0\0\0" ? undef : $val',
7880             PrintConv => {
7881             mhlr => 'Media Handler',
7882             dhlr => 'Data Handler',
7883             },
7884             },
7885             8 => {
7886             Name => 'HandlerType',
7887             Format => 'undef[4]',
7888             RawConv => q{
7889             $$self{HandlerType} = $val unless $val eq 'alis' or $val eq 'url ';
7890             $$self{HasHandler}{$val} = 1; # remember all our handlers
7891             return $val;
7892             },
7893             PrintConvColumns => 2,
7894             PrintConv => {
7895             alis => 'Alias Data', #PH
7896             crsm => 'Clock Reference', #3
7897             hint => 'Hint Track',
7898             ipsm => 'IPMP', #3
7899             m7sm => 'MPEG-7 Stream', #3
7900             meta => 'NRT Metadata', #PH
7901             mdir => 'Metadata', #3
7902             mdta => 'Metadata Tags', #PH
7903             mjsm => 'MPEG-J', #3
7904             ocsm => 'Object Content', #3
7905             odsm => 'Object Descriptor', #3
7906             priv => 'Private', #PH
7907             sdsm => 'Scene Description', #3
7908             soun => 'Audio Track',
7909             text => 'Text', #PH (but what type? subtitle?)
7910             tmcd => 'Time Code', #PH
7911             'url '=> 'URL', #3
7912             vide => 'Video Track',
7913             subp => 'Subpicture', #http://www.google.nl/patents/US7778526
7914             nrtm => 'Non-Real Time Metadata', #PH (Sony ILCE-7S) [how is this different from "meta"?]
7915             pict => 'Picture', # (HEIC images)
7916             camm => 'Camera Metadata', # (Insta360 MP4)
7917             psmd => 'Panasonic Static Metadata', #PH (Leica C-Lux CAM-DC25)
7918             data => 'Data', #PH (GPS and G-sensor data from DataKam)
7919             sbtl => 'Subtitle', #PH (TomTom Bandit Action Cam)
7920             },
7921             },
7922             12 => { #PH
7923             Name => 'HandlerVendorID',
7924             Format => 'undef[4]',
7925             RawConv => '$val eq "\0\0\0\0" ? undef : $val',
7926             PrintConv => \%vendorID,
7927             SeparateTable => 'VendorID',
7928             },
7929             24 => {
7930             Name => 'HandlerDescription',
7931             Format => 'string',
7932             # (sometimes this is a Pascal string, and sometimes it is a C string)
7933             RawConv => q{
7934             $val=substr($val,1,ord($1)) if $val=~/^([\0-\x1f])/ and ord($1)
7935             length $val ? $val : undef;
7936             },
7937             },
7938             );
7939              
7940             # Flip uuid data (ref PH)
7941             %Image::ExifTool::QuickTime::Flip = (
7942             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7943             FORMAT => 'int32u',
7944             FIRST_ENTRY => 0,
7945             NOTES => 'Found in MP4 files from Flip Video cameras.',
7946             GROUPS => { 1 => 'MakerNotes', 2 => 'Image' },
7947             1 => 'PreviewImageWidth',
7948             2 => 'PreviewImageHeight',
7949             13 => 'PreviewImageLength',
7950             14 => { # (confirmed for FlipVideoMinoHD)
7951             Name => 'SerialNumber',
7952             Groups => { 2 => 'Camera' },
7953             Format => 'string[16]',
7954             },
7955             28 => {
7956             Name => 'PreviewImage',
7957             Groups => { 2 => 'Preview' },
7958             Format => 'undef[$val{13}]',
7959             RawConv => '$self->ValidateImage(\$val, $tag)',
7960             },
7961             );
7962              
7963             # atoms in Pittasoft "free" atom
7964             %Image::ExifTool::QuickTime::Pittasoft = (
7965             PROCESS_PROC => \&ProcessMOV,
7966             NOTES => 'Tags found in Pittasoft Blackvue dashcam "free" data.',
7967             cprt => 'Copyright',
7968             thum => {
7969             Name => 'PreviewImage',
7970             Groups => { 2 => 'Preview' },
7971             Binary => 1,
7972             RawConv => q{
7973             return undef unless length $val > 4;
7974             my $len = unpack('N', $val);
7975             return undef unless length $val >= 4 + $len;
7976             return substr($val, 4, $len);
7977             },
7978             },
7979             ptnm => {
7980             Name => 'OriginalFileName',
7981             ValueConv => 'substr($val, 4, -1)',
7982             },
7983             ptrh => {
7984             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Pittasoft' },
7985             # contains these atoms:
7986             # ptvi - 27 bytes: '..avc1...'
7987             # ptso - 16 bytes: '..mp4a...'
7988             },
7989             'gps ' => {
7990             Name => 'GPSLog',
7991             Binary => 1, # (ASCII NMEA track log with leading timestamps)
7992             Notes => 'parsed to extract GPS separately when ExtractEmbedded is used',
7993             RawConv => q{
7994             $val =~ s/\0+$//; # remove trailing nulls
7995             if (length $val and $$self{OPTIONS}{ExtractEmbedded}) {
7996             my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
7997             Image::ExifTool::QuickTime::ProcessGPSLog($self, { DataPt => \$val }, $tagTbl);
7998             }
7999             return $val;
8000             },
8001             },
8002             '3gf ' => {
8003             Name => 'AccelData',
8004             SubDirectory => {
8005             TagTable => 'Image::ExifTool::QuickTime::Stream',
8006             ProcessProc => \&Process_3gf,
8007             },
8008             },
8009             sttm => {
8010             Name => 'StartTime',
8011             Format => 'int64u',
8012             Groups => { 2 => 'Time' },
8013             RawConv => '$$self{StartTime} = $val',
8014             # (ms since Jan 1, 1970, in local time zone - PH)
8015             ValueConv => q{
8016             my $secs = int($val / 1000);
8017             return ConvertUnixTime($secs) . sprintf(".%03d",$val - $secs * 1000);
8018             },
8019             PrintConv => '$self->ConvertDateTime($val)',
8020             },
8021             );
8022              
8023             # QuickTime composite tags
8024             %Image::ExifTool::QuickTime::Composite = (
8025             GROUPS => { 2 => 'Video' },
8026             Rotation => {
8027             Notes => q{
8028             writing this tag updates QuickTime MatrixStructure for all tracks with a
8029             non-zero image size
8030             },
8031             Require => {
8032             0 => 'QuickTime:MatrixStructure',
8033             1 => 'QuickTime:HandlerType',
8034             },
8035             Writable => 1,
8036             Protected => 1,
8037             WriteAlso => {
8038             MatrixStructure => 'Image::ExifTool::QuickTime::GetRotationMatrix($val)',
8039             },
8040             ValueConv => 'Image::ExifTool::QuickTime::CalcRotation($self)',
8041             ValueConvInv => '$val',
8042             },
8043             AvgBitrate => {
8044             Priority => 0, # let QuickTime::AvgBitrate take priority
8045             Require => {
8046             0 => 'QuickTime::MediaDataSize',
8047             1 => 'QuickTime::Duration',
8048             },
8049             RawConv => q{
8050             return undef unless $val[1];
8051             $val[1] /= $$self{TimeScale} if $$self{TimeScale};
8052             my $key = 'MediaDataSize';
8053             my $size = $val[0];
8054             for (;;) {
8055             $key = $self->NextTagKey($key) or last;
8056             $size += $self->GetValue($key, 'ValueConv');
8057             }
8058             return int($size * 8 / $val[1] + 0.5);
8059             },
8060             PrintConv => 'ConvertBitrate($val)',
8061             },
8062             GPSLatitude => {
8063             Require => 'QuickTime:GPSCoordinates',
8064             Groups => { 2 => 'Location' },
8065             ValueConv => 'my @c = split " ", $val; $c[0]',
8066             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
8067             },
8068             GPSLongitude => {
8069             Require => 'QuickTime:GPSCoordinates',
8070             Groups => { 2 => 'Location' },
8071             ValueConv => 'my @c = split " ", $val; $c[1]',
8072             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
8073             },
8074             # split altitude into GPSAltitude/GPSAltitudeRef like EXIF and XMP
8075             GPSAltitude => {
8076             Require => 'QuickTime:GPSCoordinates',
8077             Groups => { 2 => 'Location' },
8078             Priority => 0, # (because it may not exist)
8079             ValueConv => 'my @c = split " ", $val; defined $c[2] ? abs($c[2]) : undef',
8080             PrintConv => '"$val m"',
8081             },
8082             GPSAltitudeRef => {
8083             Require => 'QuickTime:GPSCoordinates',
8084             Groups => { 2 => 'Location' },
8085             Priority => 0, # (because altitude information may not exist)
8086             ValueConv => 'my @c = split " ", $val; defined $c[2] ? ($c[2] < 0 ? 1 : 0) : undef',
8087             PrintConv => {
8088             0 => 'Above Sea Level',
8089             1 => 'Below Sea Level',
8090             },
8091             },
8092             GPSLatitude2 => {
8093             Name => 'GPSLatitude',
8094             Require => 'QuickTime:LocationInformation',
8095             Groups => { 2 => 'Location' },
8096             ValueConv => '$val =~ /Lat=([-+.\d]+)/; $1',
8097             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
8098             },
8099             GPSLongitude2 => {
8100             Name => 'GPSLongitude',
8101             Require => 'QuickTime:LocationInformation',
8102             Groups => { 2 => 'Location' },
8103             ValueConv => '$val =~ /Lon=([-+.\d]+)/; $1',
8104             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
8105             },
8106             GPSAltitude2 => {
8107             Name => 'GPSAltitude',
8108             Require => 'QuickTime:LocationInformation',
8109             Groups => { 2 => 'Location' },
8110             ValueConv => '$val =~ /Alt=([-+.\d]+)/; abs($1)',
8111             PrintConv => '"$val m"',
8112             },
8113             GPSAltitudeRef2 => {
8114             Name => 'GPSAltitudeRef',
8115             Require => 'QuickTime:LocationInformation',
8116             Groups => { 2 => 'Location' },
8117             ValueConv => '$val =~ /Alt=([-+.\d]+)/; $1 < 0 ? 1 : 0',
8118             PrintConv => {
8119             0 => 'Above Sea Level',
8120             1 => 'Below Sea Level',
8121             },
8122             },
8123             CDDBDiscPlayTime => {
8124             Require => 'CDDB1Info',
8125             Groups => { 2 => 'Audio' },
8126             ValueConv => '$val =~ /^..([a-z0-9]{4})/i ? hex($1) : undef',
8127             PrintConv => 'ConvertDuration($val)',
8128             },
8129             CDDBDiscTracks => {
8130             Require => 'CDDB1Info',
8131             Groups => { 2 => 'Audio' },
8132             ValueConv => '$val =~ /^.{6}([a-z0-9]{2})/i ? hex($1) : undef',
8133             },
8134             );
8135              
8136             # add our composite tags
8137             Image::ExifTool::AddCompositeTags('Image::ExifTool::QuickTime');
8138              
8139              
8140             #------------------------------------------------------------------------------
8141             # AutoLoad our routines when necessary
8142             #
8143             sub AUTOLOAD
8144             {
8145             # (Note: no need to autoload routines in QuickTimeStream that use Stream table)
8146 22 50   22   136 if ($AUTOLOAD eq 'Image::ExifTool::QuickTime::Process_mebx') {
8147 0         0 require 'Image/ExifTool/QuickTimeStream.pl';
8148 30     30   335 no strict 'refs';
  30         106  
  30         421391  
8149 0         0 return &$AUTOLOAD(@_);
8150             } else {
8151 22         187 return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
8152             }
8153             }
8154              
8155             #------------------------------------------------------------------------------
8156             # Get rotation matrix
8157             # Inputs: 0) angle in degrees
8158             # Returns: 9-element rotation matrix as a string (with 0 x/y offsets)
8159             sub GetRotationMatrix($)
8160             {
8161 1     1 0 7 my $ang = 3.14159265358979323846264 * shift() / 180;
8162 1         35 my $cos = cos $ang;
8163 1         6 my $sin = sin $ang;
8164             # round to zero
8165 1 50       8 $cos = 0 if abs($cos) < 1e-12;
8166 1 50       6 $sin = 0 if abs($sin) < 1e-12;
8167 1         3 my $msn = -$sin;
8168 1         16 return "$cos $sin 0 $msn $cos 0 0 0 1";
8169             }
8170              
8171             #------------------------------------------------------------------------------
8172             # Get rotation angle from a matrix
8173             # Inputs: 0) rotation matrix as a string
8174             # Return: positive rotation angle in degrees rounded to 3 decimal points,
8175             # or undef on error
8176             sub GetRotationAngle($)
8177             {
8178 5     5 0 16 my $rotMatrix = shift;
8179 5         36 my @a = split ' ', $rotMatrix;
8180 5 50 66     35 return undef if $a[0]==0 and $a[1]==0;
8181             # calculate the rotation angle (assume uniform rotation)
8182 5         44 my $angle = atan2($a[1], $a[0]) * 180 / 3.14159;
8183 5 100       31 $angle += 360 if $angle < 0;
8184 5         59 return int($angle * 1000 + 0.5) / 1000;
8185             }
8186              
8187             #------------------------------------------------------------------------------
8188             # Calculate rotation of video track
8189             # Inputs: 0) ExifTool object ref
8190             # Returns: rotation angle or undef
8191             sub CalcRotation($)
8192             {
8193 5     5 0 16 my $et = shift;
8194 5         16 my $value = $$et{VALUE};
8195 5         12 my ($i, $track);
8196             # get the video track family 1 group (eg. "Track1");
8197 5         18 for ($i=0; ; ++$i) {
8198 11 100       43 my $idx = $i ? " ($i)" : '';
8199 11         26 my $tag = "HandlerType$idx";
8200 11 100       38 last unless $$value{$tag};
8201 10 100       38 next unless $$value{$tag} eq 'vide';
8202 4         25 $track = $et->GetGroup($tag, 1);
8203 4         15 last;
8204             }
8205 5 100       24 return undef unless $track;
8206             # get the video track matrix
8207 4         14 for ($i=0; ; ++$i) {
8208 12 100       45 my $idx = $i ? " ($i)" : '';
8209 12         30 my $tag = "MatrixStructure$idx";
8210 12 50       41 last unless $$value{$tag};
8211 12 100       37 next unless $et->GetGroup($tag, 1) eq $track;
8212 4         24 return GetRotationAngle($$value{$tag});
8213             }
8214 0         0 return undef;
8215             }
8216              
8217             #------------------------------------------------------------------------------
8218             # Get MatrixStructure for a given rotation angle
8219             # Inputs: 0) rotation angle (deg), 1) ExifTool ref
8220             # Returns: matrix structure as a string, or undef if it can't be rotated
8221             # - requires ImageSizeLookahead to determine the video image size, and doesn't
8222             # rotate matrix unless image size is valid
8223             sub GetMatrixStructure($$)
8224             {
8225 2     2 0 5 my ($val, $et) = @_;
8226 2         15 my @a = split ' ', $val;
8227             # pass straight through if it already has an offset
8228 2 50 33     15 return $val unless $a[6] == 0 and $a[7] == 0;
8229 2         17 my @s = split ' ', $$et{ImageSizeLookahead};
8230 2         8 my ($w, $h) = @s[12,13];
8231 2 100 66     13 return undef unless $w and $h; # don't rotate 0-sized track
8232 1         8 $_ = Image::ExifTool::QuickTime::FixWrongFormat($_) foreach $w,$h;
8233             # apply necessary offsets for the standard rotations
8234 1         5 my $angle = GetRotationAngle($val);
8235 1 50       6 return undef unless defined $angle;
8236 1 50       8 if ($angle == 90) {
    50          
    50          
8237 0         0 @a[6,7] = ($h, 0);
8238             } elsif ($angle == 180) {
8239 0         0 @a[6,7] = ($w, $h);
8240             } elsif ($angle == 270) {
8241 1         4 @a[6,7] = (0, $w);
8242             }
8243 1         7 return "@a";
8244             }
8245              
8246             #------------------------------------------------------------------------------
8247             # Determine the average sample rate from a time-to-sample table
8248             # Inputs: 0) ExifTool object ref, 1) time-to-sample table data ref
8249             # Returns: average sample rate (in Hz)
8250             sub CalcSampleRate($$)
8251             {
8252 21     21 0 66 my ($et, $valPt) = @_;
8253 21         97 my @dat = unpack('N*', $$valPt);
8254 21         81 my ($num, $dur) = (0, 0);
8255 21         37 my $i;
8256 21         93 for ($i=2; $i<@dat-1; $i+=2) {
8257 21         55 $num += $dat[$i]; # total number of samples
8258 21         100 $dur += $dat[$i] * $dat[$i+1]; # total sample duration
8259             }
8260 21 50 33     186 return undef unless $num and $dur and $$et{MediaTS};
      33        
8261 21         247 return $num * $$et{MediaTS} / $dur;
8262             }
8263              
8264             #------------------------------------------------------------------------------
8265             # Fix incorrect format for ImageWidth/Height as written by Pentax
8266             sub FixWrongFormat($)
8267             {
8268 78     78 0 157 my $val = shift;
8269 78 100       231 return undef unless $val;
8270 44 50       261 return $val & 0xfff00000 ? unpack('n',pack('N',$val)) : $val;
8271             }
8272              
8273             #------------------------------------------------------------------------------
8274             # Convert ISO 6709 string to standard lag/lon format
8275             # Inputs: 0) ISO 6709 string (lat, lon, and optional alt)
8276             # Returns: position in decimal degrees with altitude if available
8277             # Notes: Wikipedia indicates altitude may be in feet -- how is this specified?
8278             sub ConvertISO6709($)
8279             {
8280 0     0 0 0 my $val = shift;
8281 0 0       0 if ($val =~ /^([-+]\d{1,2}(?:\.\d*)?)([-+]\d{1,3}(?:\.\d*)?)([-+]\d+(?:\.\d*)?)?/) {
    0          
    0          
8282             # +DD.DDD+DDD.DDD+AA.AAA
8283 0         0 $val = ($1 + 0) . ' ' . ($2 + 0);
8284 0 0       0 $val .= ' ' . ($3 + 0) if $3;
8285             } elsif ($val =~ /^([-+])(\d{2})(\d{2}(?:\.\d*)?)([-+])(\d{3})(\d{2}(?:\.\d*)?)([-+]\d+(?:\.\d*)?)?/) {
8286             # +DDMM.MMM+DDDMM.MMM+AA.AAA
8287 0         0 my $lat = $2 + $3 / 60;
8288 0 0       0 $lat = -$lat if $1 eq '-';
8289 0         0 my $lon = $5 + $6 / 60;
8290 0 0       0 $lon = -$lon if $4 eq '-';
8291 0         0 $val = "$lat $lon";
8292 0 0       0 $val .= ' ' . ($7 + 0) if $7;
8293             } elsif ($val =~ /^([-+])(\d{2})(\d{2})(\d{2}(?:\.\d*)?)([-+])(\d{3})(\d{2})(\d{2}(?:\.\d*)?)([-+]\d+(?:\.\d*)?)?/) {
8294             # +DDMMSS.SSS+DDDMMSS.SSS+AA.AAA
8295 0         0 my $lat = $2 + $3 / 60 + $4 / 3600;
8296 0 0       0 $lat = -$lat if $1 eq '-';
8297 0         0 my $lon = $6 + $7 / 60 + $8 / 3600;
8298 0 0       0 $lon = -$lon if $5 eq '-';
8299 0         0 $val = "$lat $lon";
8300 0 0       0 $val .= ' ' . ($9 + 0) if $9;
8301             }
8302 0         0 return $val;
8303             }
8304              
8305             #------------------------------------------------------------------------------
8306             # Convert Nero chapter list (ref ffmpeg libavformat/movenc.c)
8307             # Inputs: 0) binary chpl data
8308             # Returns: chapter list
8309             sub ConvertChapterList($)
8310             {
8311 0     0 0 0 my $val = shift;
8312 0         0 my $size = length $val;
8313 0 0       0 return '' if $size < 9;
8314 0         0 my $num = Get8u(\$val, 8);
8315 0         0 my ($i, @chapters);
8316 0         0 my $pos = 9;
8317 0         0 for ($i=0; $i<$num; ++$i) {
8318 0 0       0 last if $pos + 9 > $size;
8319 0         0 my $dur = Get64u(\$val, $pos) / 10000000;
8320 0         0 my $len = Get8u(\$val, $pos + 8);
8321 0 0       0 last if $pos + 9 + $len > $size;
8322 0         0 my $title = substr($val, $pos + 9, $len);
8323 0         0 $pos += 9 + $len;
8324 0         0 push @chapters, "$dur $title";
8325             }
8326 0         0 return \@chapters; # return as a list
8327             }
8328              
8329             #------------------------------------------------------------------------------
8330             # Print conversion for a Nero chapter list item
8331             # Inputs: 0) ValueConv chapter string
8332             # Returns: formatted chapter string
8333             sub PrintChapter($)
8334             {
8335 0     0 0 0 my $val = shift;
8336 0 0       0 $val =~ /^(\S+) (.*)/ or return $val;
8337 0         0 my ($dur, $title) = ($1, $2);
8338 0         0 my $h = int($dur / 3600);
8339 0         0 $dur -= $h * 3600;
8340 0         0 my $m = int($dur / 60);
8341 0         0 my $s = $dur - $m * 60;
8342 0         0 my $ss = sprintf('%06.3f', $s);
8343 0 0       0 if ($ss >= 60) {
8344 0         0 $ss = '00.000';
8345 0 0       0 ++$m >= 60 and $m -= 60, ++$h;
8346             }
8347 0         0 return sprintf("[%d:%.2d:%s] %s",$h,$m,$ss,$title);
8348             }
8349              
8350             #------------------------------------------------------------------------------
8351             # Format GPSCoordinates for printing
8352             # Inputs: 0) string with numerical lat, lon and optional alt, separated by spaces
8353             # 1) ExifTool object reference
8354             # Returns: PrintConv value
8355             sub PrintGPSCoordinates($)
8356             {
8357 0     0 0 0 my ($val, $et) = @_;
8358 0         0 my @v = split ' ', $val;
8359 0         0 my $prt = Image::ExifTool::GPS::ToDMS($et, $v[0], 1, "N") . ', ' .
8360             Image::ExifTool::GPS::ToDMS($et, $v[1], 1, "E");
8361 0 0       0 if (defined $v[2]) {
8362 0 0       0 $prt .= ', ' . ($v[2] < 0 ? -$v[2] . ' m Below' : $v[2] . ' m Above') . ' Sea Level';
8363             }
8364 0         0 return $prt;
8365             }
8366              
8367             #------------------------------------------------------------------------------
8368             # Unpack packed ISO 639/T language code
8369             # Inputs: 0) packed language code (or undef/0), 1) true to not treat 'und' and 'eng' as default
8370             # Returns: language code, or undef/0 for default language, or 'err' for format error
8371             sub UnpackLang($;$)
8372             {
8373 10     10 0 26 my ($lang, $noDef) = @_;
8374 10 50       24 if ($lang) {
8375             # language code is packed in 5-bit characters
8376 10         24 $lang = pack 'C*', map { (($lang>>$_)&0x1f)+0x60 } 10, 5, 0;
  30         85  
8377             # validate language code
8378 10 50       51 if ($lang =~ /^[a-z]+$/) {
8379             # treat 'eng' or 'und' as the default language
8380 10 50 66     62 undef $lang if ($lang eq 'und' or $lang eq 'eng') and not $noDef;
      66        
8381             } else {
8382 0         0 $lang = 'err'; # invalid language code
8383             }
8384             }
8385 10         24 return $lang;
8386             }
8387              
8388             #------------------------------------------------------------------------------
8389             # Get language code string given QuickTime language and country codes
8390             # Inputs: 0) numerical language code, 1) numerical country code, 2) no defaults
8391             # Returns: language code string (ie. "fra-FR") or undef for default language
8392             sub GetLangCode($;$$)
8393             {
8394 6     6 0 17 my ($lang, $ctry, $noDef) = @_;
8395             # ignore country ('ctry') and language lists ('lang') for now
8396 6 50 66     24 undef $ctry if $ctry and $ctry <= 255;
8397 6 50 33     29 undef $lang if $lang and $lang <= 255;
8398 6         21 $lang = UnpackLang($lang, $noDef);
8399             # add country code if specified
8400 6 100       19 if ($ctry) {
8401 1         6 $ctry = unpack('a2',pack('n',$ctry)); # unpack as ISO 3166-1
8402             # treat 'ZZ' like a default country (see ref 12)
8403 1 50       5 undef $ctry if $ctry eq 'ZZ';
8404 1 50 33     8 if ($ctry and $ctry =~ /^[A-Z]{2}$/) {
8405 1 50       4 $lang or $lang = 'und';
8406 1         3 $lang .= "-$ctry";
8407             }
8408             }
8409 6         18 return $lang;
8410             }
8411              
8412             #------------------------------------------------------------------------------
8413             # Get langInfo hash and save details about alt-lang tags
8414             # Inputs: 0) ExifTool ref, 1) tagInfo hash ref, 2) locale code
8415             # Returns: new tagInfo hash ref, or undef if invalid
8416             sub GetLangInfoQT($$$)
8417             {
8418 1     1 0 4 my ($et, $tagInfo, $langCode) = @_;
8419 1         8 my $langInfo = Image::ExifTool::GetLangInfo($tagInfo, $langCode);
8420 1 50       5 if ($langInfo) {
8421 1 50       6 $$et{QTLang} or $$et{QTLang} = [ ];
8422 1         3 push @{$$et{QTLang}}, $$langInfo{Name};
  1         4  
8423             }
8424 1         4 return $langInfo;
8425             }
8426              
8427             #------------------------------------------------------------------------------
8428             # Get variable-length integer from data (used by ParseItemLocation)
8429             # Inputs: 0) data ref, 1) start position, 2) integer size in bytes (0, 4 or 8),
8430             # 3) default value
8431             # Returns: integer value, and updates current position
8432             sub GetVarInt($$$;$)
8433             {
8434 78     78 0 149 my ($dataPt, $pos, $n, $default) = @_;
8435 78         122 my $len = length $$dataPt;
8436 78         108 $_[1] = $pos + $n; # update current position
8437 78 50       160 return undef if $pos + $n > $len;
8438 78 50       163 if ($n == 0) {
    50          
    0          
8439 0   0     0 return $default || 0;
8440             } elsif ($n == 4) {
8441 78         172 return Get32u($dataPt, $pos);
8442             } elsif ($n == 8) {
8443 0         0 return Get64u($dataPt, $pos);
8444             }
8445 0         0 return undef;
8446             }
8447              
8448             #------------------------------------------------------------------------------
8449             # Get null-terminated string from binary data (used by ParseItemInfoEntry)
8450             # Inputs: 0) data ref, 1) start position
8451             # Returns: string, and updates current position
8452             sub GetString($$)
8453             {
8454 30     30 0 64 my ($dataPt, $pos) = @_;
8455 30         56 my $len = length $$dataPt;
8456 30         46 my $str = '';
8457 30         73 while ($pos < $len) {
8458 240         361 my $ch = substr($$dataPt, $pos, 1);
8459 240         289 ++$pos;
8460 240 100       415 last if ord($ch) == 0;
8461 215         363 $str .= $ch;
8462             }
8463 30         80 $_[1] = $pos; # update current position
8464 30         93 return $str;
8465             }
8466              
8467             #------------------------------------------------------------------------------
8468             # Get a printable version of the tag ID
8469             # Inputs: 0) tag ID, 1) Flag: 0x01 - print as 4- or 8-digit hex value if necessary
8470             # 0x02 - put leading backslash before escaped character
8471             # Returns: Printable tag ID
8472             sub PrintableTagID($;$)
8473             {
8474 22     22 0 46 my $tag = $_[0];
8475 22         67 my $n = ($tag =~ s/([\x00-\x1f\x7f-\xff])/'x'.unpack('H*',$1)/eg);
  0         0  
8476 22 0 33     61 if ($n and $_[1]) {
8477 0 0 0     0 if ($n > 2 and $_[1] & 0x01) {
    0          
8478 0         0 $tag = '0x' . unpack('H8', $_[0]);
8479 0         0 $tag =~ s/^0x0000/0x/;
8480             } elsif ($_[1] & 0x02) {
8481 0         0 ($tag = $_[0]) =~ s/([\x00-\x1f\x7f-\xff])/'\\x'.unpack('H*',$1)/eg;
  0         0  
8482             }
8483             }
8484 22         59 return $tag;
8485             }
8486              
8487             #==============================================================================
8488             # The following ParseXxx routines parse various boxes to extract this
8489             # information about embedded items in a $$et{ItemInfo} hash, keyed by item ID:
8490             #
8491             # iloc:
8492             # ConstructionMethod - offset type: 0=file, 1=idat, 2=item
8493             # DataReferenceIndex - 0 for "this file", otherwise index in dref box
8494             # BaseOffset - base for file offsets
8495             # Extents - list of details for data in file:
8496             # 0) index (extent_index)
8497             # 1) offset (extent_offset)
8498             # 2) length (extent_length)
8499             # 3) nlen (length_size)
8500             # 4) lenPt (pointer to length word)
8501             # infe:
8502             # ProtectionIndex - index if item is protected (0 for unprotected)
8503             # Name - item name
8504             # ContentType - mime type of item
8505             # ContentEncoding - item encoding
8506             # URI - URI of a 'uri '-type item
8507             # ipma:
8508             # Association - list of associated properties in the ipco container
8509             # Essential - list of "essential" flags for the associated properties
8510             # cdsc:
8511             # RefersTo - hash lookup of flags based on referred item ID
8512             # other:
8513             # DocNum - exiftool document number for this item
8514             #
8515             #------------------------------------------------------------------------------
8516             # Parse item location (iloc) box (ref ISO 14496-12:2015 pg.79)
8517             # Inputs: 0) iloc data, 1) ExifTool ref
8518             # Returns: undef, and fills in ExifTool ItemInfo hash
8519             # Notes: see also Handle_iloc() in WriteQuickTime.pl
8520             sub ParseItemLocation($$)
8521             {
8522 6     6 0 26 my ($val, $et) = @_;
8523 6         31 my ($i, $j, $num, $pos, $id);
8524 6         0 my ($extent_index, $extent_offset, $extent_length);
8525              
8526 6 100       35 my $verbose = $$et{IsWriting} ? 0 : $et->Options('Verbose');
8527 6   50     49 my $items = $$et{ItemInfo} || ($$et{ItemInfo} = { });
8528 6         19 my $len = length $val;
8529 6 50       23 return undef if $len < 8;
8530 6         22 my $ver = Get8u(\$val, 0);
8531 6         35 my $siz = Get16u(\$val, 4);
8532 6         16 my $noff = ($siz >> 12);
8533 6         22 my $nlen = ($siz >> 8) & 0x0f;
8534 6         15 my $nbas = ($siz >> 4) & 0x0f;
8535 6         18 my $nind = $siz & 0x0f;
8536 6 50       22 if ($ver < 2) {
8537 6         21 $num = Get16u(\$val, 6);
8538 6         26 $pos = 8;
8539             } else {
8540 0 0       0 return undef if $len < 10;
8541 0         0 $num = Get32u(\$val, 6);
8542 0         0 $pos = 10;
8543             }
8544 6         28 for ($i=0; $i<$num; ++$i) {
8545 20 50       54 if ($ver < 2) {
8546 20 50       62 return undef if $pos + 2 > $len;
8547 20         52 $id = Get16u(\$val, $pos);
8548 20         40 $pos += 2;
8549             } else {
8550 0 0       0 return undef if $pos + 4 > $len;
8551 0         0 $id = Get32u(\$val, $pos);
8552 0         0 $pos += 4;
8553             }
8554 20 50 33     88 if ($ver == 1 or $ver == 2) {
8555 0 0       0 return undef if $pos + 2 > $len;
8556 0         0 $$items{$id}{ConstructionMethod} = Get16u(\$val, $pos) & 0x0f;
8557 0         0 $pos += 2;
8558             }
8559 20 50       51 return undef if $pos + 2 > $len;
8560 20         48 $$items{$id}{DataReferenceIndex} = Get16u(\$val, $pos);
8561 20         40 $pos += 2;
8562 20         48 $$items{$id}{BaseOffset} = GetVarInt(\$val, $pos, $nbas);
8563 20 50       53 return undef if $pos + 2 > $len;
8564 20         45 my $ext_num = Get16u(\$val, $pos);
8565 20         37 $pos += 2;
8566 20         34 my @extents;
8567 20         55 for ($j=0; $j<$ext_num; ++$j) {
8568 20 50 33     81 if ($ver == 1 or $ver == 2) {
8569 0         0 $extent_index = GetVarInt(\$val, $pos, $nind, 1);
8570             }
8571 20         50 $extent_offset = GetVarInt(\$val, $pos, $noff);
8572 20         54 $extent_length = GetVarInt(\$val, $pos, $nlen);
8573 20 50       57 return undef unless defined $extent_length;
8574             $et->VPrint(1, "$$et{INDENT} Item $id: const_meth=",
8575             defined $$items{$id}{ConstructionMethod} ? $$items{$id}{ConstructionMethod} : '',
8576             sprintf(" base=0x%x offset=0x%x len=0x%x\n", $$items{$id}{BaseOffset},
8577 20 0       45 $extent_offset, $extent_length)) if $verbose;
    50          
8578 20         79 push @extents, [ $extent_index, $extent_offset, $extent_length, $nlen, $pos-$nlen ];
8579             }
8580             # save item location information keyed on 1-based item ID:
8581 20         73 $$items{$id}{Extents} = \@extents;
8582             }
8583 6         21 return undef;
8584             }
8585              
8586             #------------------------------------------------------------------------------
8587             # Parse content describes entry (cdsc) box
8588             # Inputs: 0) cdsc data, 1) ExifTool ref
8589             # Returns: undef, and fills in ExifTool ItemInfo hash
8590             sub ParseContentDescribes($$)
8591             {
8592 8     8 0 27 my ($val, $et) = @_;
8593 8         20 my ($id, $count, @to);
8594 8 50       26 if ($$et{ItemRefVersion}) {
8595 0 0       0 return undef if length $val < 10;
8596 0         0 ($id, $count, @to) = unpack('NnN*', $val);
8597             } else {
8598 8 50       27 return undef if length $val < 6;
8599 8         33 ($id, $count, @to) = unpack('nnn*', $val);
8600             }
8601 8 50       38 if ($count > @to) {
    50          
8602 0         0 my $str = 'Missing values in ContentDescribes box';
8603 0 0       0 $$et{IsWriting} ? $et->Error($str) : $et->Warn($str);
8604             } elsif ($count < @to) {
8605 0         0 $et->Warn('Ignored extra values in ContentDescribes box', 1);
8606 0         0 @to = $count;
8607             }
8608             # add all referenced item ID's to a "RefersTo" lookup
8609 8         54 $$et{ItemInfo}{$id}{RefersTo}{$_} = 1 foreach @to;
8610 8         25 return undef;
8611             }
8612              
8613             #------------------------------------------------------------------------------
8614             # Parse item information entry (infe) box (ref ISO 14496-12:2015 pg.82)
8615             # Inputs: 0) infe data, 1) ExifTool ref
8616             # Returns: undef, and fills in ExifTool ItemInfo hash
8617             sub ParseItemInfoEntry($$)
8618             {
8619 20     20 0 53 my ($val, $et) = @_;
8620 20         38 my $id;
8621              
8622 20 100       80 my $verbose = $$et{IsWriting} ? 0 : $et->Options('Verbose');
8623 20   50     72 my $items = $$et{ItemInfo} || ($$et{ItemInfo} = { });
8624 20         34 my $len = length $val;
8625 20 50       55 return undef if $len < 4;
8626 20         60 my $ver = Get8u(\$val, 0);
8627 20         37 my $pos = 4;
8628 20 50       52 return undef if $pos + 4 > $len;
8629 20 50 33     83 if ($ver == 0 or $ver == 1) {
8630 0         0 $id = Get16u(\$val, $pos);
8631 0         0 $$items{$id}{ProtectionIndex} = Get16u(\$val, $pos + 2);
8632 0         0 $pos += 4;
8633 0         0 $$items{$id}{Name} = GetString(\$val, $pos);
8634 0         0 $$items{$id}{ContentType} = GetString(\$val, $pos);
8635 0         0 $$items{$id}{ContentEncoding} = GetString(\$val, $pos);
8636             } else {
8637 20 50       53 if ($ver == 2) {
    0          
8638 20         50 $id = Get16u(\$val, $pos);
8639 20         50 $pos += 2;
8640             } elsif ($ver == 3) {
8641 0         0 $id = Get32u(\$val, $pos);
8642 0         0 $pos += 4;
8643             }
8644 20 50       52 return undef if $pos + 6 > $len;
8645 20         46 $$items{$id}{ProtectionIndex} = Get16u(\$val, $pos);
8646 20         50 my $type = substr($val, $pos + 2, 4);
8647 20         50 $$items{$id}{Type} = $type;
8648 20         45 $pos += 6;
8649 20         61 $$items{$id}{Name} = GetString(\$val, $pos);
8650 20 100       84 if ($type eq 'mime') {
    50          
8651 5         27 $$items{$id}{ContentType} = GetString(\$val, $pos);
8652 5         25 $$items{$id}{ContentEncoding} = GetString(\$val, $pos);
8653             } elsif ($type eq 'uri ') {
8654 0         0 $$items{$id}{URI} = GetString(\$val, $pos);
8655             }
8656             }
8657             $et->VPrint(1, "$$et{INDENT} Item $id: Type=", $$items{$id}{Type} || '',
8658             ' Name=', $$items{$id}{Name} || '',
8659 20 50 0     54 ' ContentType=', $$items{$id}{ContentType} || '',
      0        
      0        
8660             "\n") if $verbose > 1;
8661 20         51 return undef;
8662             }
8663              
8664             #------------------------------------------------------------------------------
8665             # Parse item property association (ipma) box (ref https://github.com/gpac/gpac/blob/master/src/isomedia/iff.c)
8666             # Inputs: 0) ipma data, 1) ExifTool ref
8667             # Returns: undef, and fills in ExifTool ItemInfo hash
8668             sub ParseItemPropAssoc($$)
8669             {
8670 6     6 0 24 my ($val, $et) = @_;
8671 6         17 my ($i, $j, $id);
8672              
8673 6 100       32 my $verbose = $$et{IsWriting} ? 0 : $et->Options('Verbose');
8674 6   50     26 my $items = $$et{ItemInfo} || ($$et{ItemInfo} = { });
8675 6         14 my $len = length $val;
8676 6 50       22 return undef if $len < 8;
8677 6         21 my $ver = Get8u(\$val, 0);
8678 6         24 my $flg = Get32u(\$val, 0);
8679 6         27 my $num = Get32u(\$val, 4);
8680 6         21 my $pos = 8;
8681 6         28 for ($i=0; $i<$num; ++$i) {
8682 12 50       36 if ($ver == 0) {
8683 12 50       40 return undef if $pos + 3 > $len;
8684 12         35 $id = Get16u(\$val, $pos);
8685 12         25 $pos += 2;
8686             } else {
8687 0 0       0 return undef if $pos + 5 > $len;
8688 0         0 $id = Get32u(\$val, $pos);
8689 0         0 $pos += 4;
8690             }
8691 12         37 my $n = Get8u(\$val, $pos++);
8692 12         36 my (@association, @essential);
8693 12 50       31 if ($flg & 0x01) {
8694 0 0       0 return undef if $pos + $n * 2 > $len;
8695 0         0 for ($j=0; $j<$n; ++$j) {
8696 0         0 my $tmp = Get16u(\$val, $pos + $j * 2);
8697 0         0 push @association, $tmp & 0x7fff;
8698 0 0       0 push @essential, ($tmp & 0x8000) ? 1 : 0;
8699             }
8700 0         0 $pos += $n * 2;
8701             } else {
8702 12 50       41 return undef if $pos + $n > $len;
8703 12         38 for ($j=0; $j<$n; ++$j) {
8704 24         60 my $tmp = Get8u(\$val, $pos + $j);
8705 24         60 push @association, $tmp & 0x7f;
8706 24 100       77 push @essential, ($tmp & 0x80) ? 1 : 0;
8707             }
8708 12         28 $pos += $n;
8709             }
8710 12         36 $$items{$id}{Association} = \@association;
8711 12         44 $$items{$id}{Essential} = \@essential;
8712 12 50       53 $et->VPrint(1, "$$et{INDENT} Item $id properties: @association\n") if $verbose > 1;
8713             }
8714 6         18 return undef;
8715             }
8716              
8717             #------------------------------------------------------------------------------
8718             # Process item information now
8719             # Inputs: 0) ExifTool ref
8720             sub HandleItemInfo($)
8721             {
8722 45     45 0 101 my $et = shift;
8723 45         117 my $raf = $$et{RAF};
8724 45         111 my $items = $$et{ItemInfo};
8725 45         182 my $verbose = $et->Options('Verbose');
8726 45         118 my $buff;
8727              
8728             # extract information from EXIF/XMP metadata items
8729 45 100 66     173 if ($items and $raf) {
8730 3         9 push @{$$et{PATH}}, 'ItemInformation';
  3         11  
8731 3         13 my $curPos = $raf->Tell();
8732 3         9 my $primary = $$et{PrimaryItem};
8733 3         8 my $id;
8734 3         26 $et->VerboseDir('Processing items from ItemInformation', scalar(keys %$items));
8735 3         52 foreach $id (sort { $a <=> $b } keys %$items) {
  12         37  
8736 11         24 my $item = $$items{$id};
8737 11   50     55 my $type = $$item{ContentType} || $$item{Type} || next;
8738 11 50       26 if ($verbose) {
8739             # add up total length of this item for the verbose output
8740 0         0 my $len = 0;
8741 0 0 0     0 if ($$item{Extents} and @{$$item{Extents}}) {
  0         0  
8742 0         0 $len += $$_[2] foreach @{$$item{Extents}};
  0         0  
8743             }
8744 0         0 $et->VPrint(0, "$$et{INDENT}Item $id) '${type}' ($len bytes)\n");
8745             }
8746             # get ExifTool name for this item
8747 11   100     65 my $name = { Exif => 'EXIF', 'application/rdf+xml' => 'XMP', jpeg => 'PreviewImage' }->{$type} || '';
8748 11         29 my ($warn, $extent);
8749 11 50       29 $warn = "Can't currently decode encoded $type metadata" if $$item{ContentEncoding};
8750 11 50       25 $warn = "Can't currently decode protected $type metadata" if $$item{ProtectionIndex};
8751 11 50       27 $warn = "Can't currently extract $type with construction method $$item{ConstructionMethod}" if $$item{ConstructionMethod};
8752 11 50 33     32 $et->WarnOnce($warn) if $warn and $name;
8753 11 50       26 $warn = 'Not this file' if $$item{DataReferenceIndex}; # (can only extract from "this file")
8754 11 50 33     37 unless (($$item{Extents} and @{$$item{Extents}}) or $warn) {
  11   33     40  
8755 0         0 $warn = "No Extents for $type item";
8756 0 0       0 $et->WarnOnce($warn) if $name;
8757             }
8758 11 50       25 if ($warn) {
8759 0 0       0 $et->VPrint(0, "$$et{INDENT} [not extracted] ($warn)\n") if $verbose > 2;
8760 0         0 next;
8761             }
8762 11   100     35 my $base = $$item{BaseOffset} || 0;
8763 11 50       28 if ($verbose > 2) {
8764             # do verbose hex dump
8765 0         0 my $len = 0;
8766 0         0 undef $buff;
8767 0         0 my $val = '';
8768 0 0       0 my $maxLen = $verbose > 3 ? 2048 : 96;
8769 0         0 foreach $extent (@{$$item{Extents}}) {
  0         0  
8770 0         0 my $n = $$extent[2];
8771 0         0 my $more = $maxLen - $len;
8772 0 0 0     0 if ($more > 0 and $n) {
8773 0 0       0 $more = $n if $more > $n;
8774 0 0       0 $val .= $buff if defined $buff;
8775 0 0       0 $raf->Seek($$extent[1] + $base, 0) or last;
8776 0 0       0 $raf->Read($buff, $more) or last;
8777             }
8778 0         0 $len += $n;
8779             }
8780 0 0       0 if (defined $buff) {
8781 0 0       0 $buff = $val . $buff if length $val;
8782 0         0 $et->VerboseDump(\$buff, DataPos => $$item{Extents}[0][1] + $base);
8783 0         0 my $snip = $len - length $buff;
8784 0 0       0 $et->VPrint(0, "$$et{INDENT} [snip $snip bytes]\n") if $snip;
8785             }
8786             }
8787             # do MD5 checksum of AVIF "av01" image data
8788 11 0 33     33 if ($type eq 'av01' and $$et{ImageDataMD5}) {
8789 0         0 my $md5 = $$et{ImageDataMD5};
8790 0         0 my $tot = 0;
8791 0         0 foreach $extent (@{$$item{Extents}}) {
  0         0  
8792 0 0       0 $raf->Seek($$extent[1] + $base, 0) or $et->Warn('Seek error in av01 image data'), last;
8793 0         0 $tot += $et->ImageDataMD5($raf, $$extent[2], 'av01 image', 1);
8794             }
8795 0 0       0 $et->VPrint(0, "$$et{INDENT}(ImageDataMD5: $tot bytes of av01 data)\n") if $tot;
8796             }
8797 11 100       26 next unless $name;
8798             # assemble the data for this item
8799 5         10 undef $buff;
8800 5         13 my $val = '';
8801 5         6 foreach $extent (@{$$item{Extents}}) {
  5         18  
8802 5 50       16 $val .= $buff if defined $buff;
8803 5 50       22 $raf->Seek($$extent[1] + $base, 0) or last;
8804 5 100       24 $raf->Read($buff, $$extent[2]) or last;
8805             }
8806 5 50       16 next unless defined $buff;
8807 5 50       17 $buff = $val . $buff if length $val;
8808 5 100       12 next unless length $buff; # ignore empty directories
8809 4         9 my ($start, $subTable, $proc);
8810 4         16 my $pos = $$item{Extents}[0][1] + $base;
8811 4 100 66     27 if ($name eq 'EXIF' and length $buff >= 4) {
    50          
8812 2 50       17 if ($buff =~ /^(MM\0\x2a|II\x2a\0)/) {
    50          
8813 0         0 $et->Warn('Missing Exif header');
8814 0         0 $start = 0;
8815             } elsif ($buff =~ /^Exif\0\0/) {
8816             # (haven't seen this yet, but it is just a matter of time
8817             # until someone screws it up like this)
8818 0         0 $et->Warn('Missing Exif header size');
8819 0         0 $start = 6;
8820             } else {
8821 2         10 my $n = unpack('N', $buff);
8822 2         7 $start = 4 + $n; # skip "Exif\0\0" header if it exists
8823 2 50       7 if ($start > length($buff)) {
8824 0         0 $et->Warn('Invalid EXIF header');
8825 0         0 next;
8826             }
8827 2 50       9 if ($$et{HTML_DUMP}) {
8828 0         0 $et->HDump($pos, 4, 'Exif header length', "Value: $n");
8829 0 0       0 $et->HDump($pos+4, $start-4, 'Exif header') if $n;
8830             }
8831             }
8832 2         8 $subTable = GetTagTable('Image::ExifTool::Exif::Main');
8833 2         11 $proc = \&Image::ExifTool::ProcessTIFF;
8834             } elsif ($name eq 'PreviewImage') {
8835             # take a quick stab at determining the size of the image
8836             # (based on JPEG previews found in Fuji X-H2S HIF images)
8837 0         0 my $type = 'PreviewImage';
8838 0 0       0 if ($buff =~ /^.{556}\xff\xc0\0\x11.(.{4})/s) {
8839 0         0 my ($h, $w) = unpack('n2', $1);
8840             # (not sure if $h is ever the long dimension, but test it just in case)
8841 0 0 0     0 if ($w == 160 or $h == 160) {
    0 0        
8842 0         0 $type = 'ThumbnailImage';
8843             } elsif ($w == 1920 or $h == 1920) {
8844 0         0 $type = 'OtherImage'; # (large preview)
8845             } # (PreviewImage is 640x480)
8846             }
8847 0         0 $et->FoundTag($type => $buff);
8848 0         0 next;
8849             } else {
8850 2         3 $start = 0;
8851 2         9 $subTable = GetTagTable('Image::ExifTool::XMP::Main');
8852             }
8853 4         31 my %dirInfo = (
8854             DataPt => \$buff,
8855             DataLen => length $buff,
8856             DirStart => $start,
8857             DirLen => length($buff) - $start,
8858             DataPos => $pos,
8859             Base => $pos + $start, # (needed for HtmlDump and IsOffset tags in binary data)
8860             );
8861             # handle processing of metadata for sub-documents
8862 4 50 33     42 if (defined $primary and $$item{RefersTo} and not $$item{RefersTo}{$primary}) {
      33        
8863             # set document number if this doesn't refer to the primary document
8864 0         0 $$et{DOC_NUM} = ++$$et{DOC_COUNT};
8865             # associate this document number with the lowest item index
8866 0         0 my ($lowest) = sort { $a <=> $b } keys %{$$item{RefersTo}};
  0         0  
  0         0  
8867 0         0 $$items{$lowest}{DocNum} = $$et{DOC_NUM};
8868             }
8869 4         20 $et->ProcessDirectory(\%dirInfo, $subTable, $proc);
8870 4         21 delete $$et{DOC_NUM};
8871             }
8872 3         19 $raf->Seek($curPos, 0); # seek back to original position
8873 3         6 pop @{$$et{PATH}};
  3         12  
8874             }
8875             # process the item properties now that we should know their associations and document numbers
8876 45 100       175 if ($$et{ItemPropertyContainer}) {
8877 3         9 my ($dirInfo, $subTable, $proc) = @{$$et{ItemPropertyContainer}};
  3         13  
8878 3         10 $$et{IsItemProperty} = 1; # set item property flag
8879 3         16 $et->ProcessDirectory($dirInfo, $subTable, $proc);
8880 3         10 delete $$et{ItemPropertyContainer};
8881 3         11 delete $$et{IsItemProperty};
8882 3         14 delete $$et{DOC_NUM};
8883             }
8884 45         151 delete $$et{ItemInfo};
8885             }
8886              
8887             #------------------------------------------------------------------------------
8888             # Warn if ExtractEmbedded option isn't used
8889             # Inputs: 0) ExifTool ref
8890             sub EEWarn($)
8891             {
8892 0     0 0 0 my $et = shift;
8893 0         0 $et->WarnOnce('The ExtractEmbedded option may find more tags in the media data',3);
8894             }
8895              
8896             #------------------------------------------------------------------------------
8897             # Get quicktime format from flags word
8898             # Inputs: 0) quicktime atom flags, 1) data length
8899             # Returns: ExifTool format string
8900             sub QuickTimeFormat($$)
8901             {
8902 12     12 0 34 my ($flags, $len) = @_;
8903 12         20 my $format;
8904 12 50 33     115 if ($flags == 0x15 or $flags == 0x16) {
    50          
    50          
    50          
8905 0         0 $format = { 1=>'int8', 2=>'int16', 4=>'int32', 8=>'int64' }->{$len};
8906 0 0       0 $format .= $flags == 0x15 ? 's' : 'u' if $format;
    0          
8907             } elsif ($flags == 0x17) {
8908 0         0 $format = 'float';
8909             } elsif ($flags == 0x18) {
8910 0         0 $format = 'double';
8911             } elsif ($flags == 0x00) {
8912 0         0 $format = { 1=>'int8u', 2=>'int16u' }->{$len};
8913             }
8914 12         29 return $format;
8915             }
8916              
8917             #------------------------------------------------------------------------------
8918             # Process MPEG-4 MTDT atom (ref 11)
8919             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
8920             # Returns: 1 on success
8921             sub ProcessMetaData($$$)
8922             {
8923 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
8924 0         0 my $dataPt = $$dirInfo{DataPt};
8925 0         0 my $dirLen = length $$dataPt;
8926 0         0 my $verbose = $et->Options('Verbose');
8927 0 0       0 return 0 unless $dirLen >= 2;
8928 0         0 my $count = Get16u($dataPt, 0);
8929 0 0       0 $verbose and $et->VerboseDir('MetaData', $count);
8930 0         0 my $i;
8931 0         0 my $pos = 2;
8932 0         0 for ($i=0; $i<$count; ++$i) {
8933 0 0       0 last if $pos + 10 > $dirLen;
8934 0         0 my $size = Get16u($dataPt, $pos);
8935 0 0 0     0 last if $size < 10 or $size + $pos > $dirLen;
8936 0         0 my $tag = Get32u($dataPt, $pos + 2);
8937 0         0 my $lang = Get16u($dataPt, $pos + 6);
8938 0         0 my $enc = Get16u($dataPt, $pos + 8);
8939 0         0 my $val = substr($$dataPt, $pos + 10, $size);
8940 0         0 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
8941 0 0       0 if ($tagInfo) {
8942             # convert language code to ASCII (ignore read-only bit)
8943 0         0 $lang = UnpackLang($lang);
8944             # handle alternate languages
8945 0 0       0 if ($lang) {
8946 0         0 my $langInfo = GetLangInfoQT($et, $tagInfo, $lang);
8947 0 0       0 $tagInfo = $langInfo if $langInfo;
8948             }
8949 0 0       0 $verbose and $et->VerboseInfo($tag, $tagInfo,
8950             Value => $val,
8951             DataPt => $dataPt,
8952             Start => $pos + 10,
8953             Size => $size - 10,
8954             );
8955             # convert from UTF-16 BE if necessary
8956 0 0       0 $val = $et->Decode($val, 'UCS2') if $enc == 1;
8957 0 0 0     0 if ($enc == 0 and $$tagInfo{Unknown}) {
8958             # binary data
8959 0         0 $et->FoundTag($tagInfo, \$val);
8960             } else {
8961 0         0 $et->FoundTag($tagInfo, $val);
8962             }
8963             }
8964 0         0 $pos += $size;
8965             }
8966 0         0 return 1;
8967             }
8968              
8969             #------------------------------------------------------------------------------
8970             # Process sample description table
8971             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
8972             # Returns: 1 on success
8973             # (ref https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25691)
8974             sub ProcessSampleDesc($$$)
8975             {
8976 38     38 0 118 my ($et, $dirInfo, $tagTablePtr) = @_;
8977 38         121 my $dataPt = $$dirInfo{DataPt};
8978 38   50     173 my $pos = $$dirInfo{DirStart} || 0;
8979 38   33     126 my $dirLen = $$dirInfo{DirLen} || (length($$dataPt) - $pos);
8980 38 50       116 return 0 if $pos + 8 > $dirLen;
8981              
8982 38         132 my $num = Get32u($dataPt, 4); # get number of sample entries in table
8983 38         103 $pos += 8;
8984 38         86 my ($i, $err);
8985 38         161 for ($i=0; $i<$num; ++$i) { # loop through sample entries
8986 38 50       130 $pos + 8 > $dirLen and $err = 1, last;
8987 38         105 my $size = Get32u($dataPt, $pos);
8988 38 50       138 $pos + $size > $dirLen and $err = 1, last;
8989 38         113 $$dirInfo{DirStart} = $pos;
8990 38         80 $$dirInfo{DirLen} = $size;
8991 38         155 ProcessHybrid($et, $dirInfo, $tagTablePtr);
8992 38         140 $pos += $size;
8993             }
8994 38 0 33     115 if ($err and $$et{HandlerType}) {
8995 0   0     0 my $grp = $$et{SET_GROUP1} || $$dirInfo{Parent} || 'unknown';
8996 0         0 $et->Warn("Truncated $$et{HandlerType} sample table for $grp");
8997             }
8998 38         120 return 1;
8999             }
9000              
9001             #------------------------------------------------------------------------------
9002             # Process hybrid binary data + QuickTime container (ref PH)
9003             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9004             # Returns: 1 on success
9005             sub ProcessHybrid($$$)
9006             {
9007 38     38 0 94 my ($et, $dirInfo, $tagTablePtr) = @_;
9008             # brute-force search for child atoms after first 8 bytes of binary data
9009 38         92 my $dataPt = $$dirInfo{DataPt};
9010 38   50     127 my $dirStart = $$dirInfo{DirStart} || 0;
9011 38   33     122 my $dirLen = $$dirInfo{DirLen} || length($$dataPt) - $dirStart;
9012 38         83 my $end = $dirStart + $dirLen;
9013 38         92 my $pos = $dirStart + 8; # skip length/version
9014 38         76 my $try = $pos;
9015 38         64 my $childPos;
9016              
9017 38         136 while ($pos <= $end - 8) {
9018 2204         3505 my $tag = substr($$dataPt, $try+4, 4);
9019             # look only for well-behaved tag ID's
9020 2204 100       5662 $tag =~ /[^\w ]/ and $try = ++$pos, next;
9021 94         232 my $size = Get32u($dataPt, $try);
9022 94 100       262 if ($size + $try == $end) {
9023             # the atom ends exactly at the end of the parent -- this must be it
9024 16         38 $childPos = $pos;
9025 16         59 $$dirInfo{DirLen} = $pos; # the binary data ends at the first child atom
9026 16         39 last;
9027             }
9028 78 100 100     298 if ($size < 8 or $size + $try > $end - 8) {
9029 58         126 $try = ++$pos; # fail. try next position
9030             } else {
9031 20         53 $try += $size; # could be another atom following this
9032             }
9033             }
9034             # process binary data
9035 38         117 $$dirInfo{MixedTags} = 1; # ignore non-integer tag ID's
9036 38         172 $et->ProcessBinaryData($dirInfo, $tagTablePtr);
9037             # process child atoms if found
9038 38 100       140 if ($childPos) {
9039 16         53 $$dirInfo{DirStart} = $childPos;
9040 16         45 $$dirInfo{DirLen} = $end - $childPos;
9041 16         60 ProcessMOV($et, $dirInfo, $tagTablePtr);
9042             }
9043 38         117 return 1;
9044             }
9045              
9046             #------------------------------------------------------------------------------
9047             # Process iTunes 'righ' atom (ref PH)
9048             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9049             # Returns: 1 on success
9050             sub ProcessRights($$$)
9051             {
9052 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
9053 0         0 my $dataPt = $$dirInfo{DataPt};
9054 0         0 my $dataPos = $$dirInfo{Base};
9055 0         0 my $dirLen = length $$dataPt;
9056 0   0     0 my $unknown = $$et{OPTIONS}{Unknown} || $$et{OPTIONS}{Verbose};
9057 0         0 my $pos;
9058 0         0 $et->VerboseDir('righ', $dirLen / 8);
9059 0         0 for ($pos = 0; $pos + 8 <= $dirLen; $pos += 8) {
9060 0         0 my $tag = substr($$dataPt, $pos, 4);
9061 0 0       0 last if $tag eq "\0\0\0\0";
9062 0         0 my $val = substr($$dataPt, $pos + 4, 4);
9063 0         0 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
9064 0 0       0 unless ($tagInfo) {
9065 0 0       0 next unless $unknown;
9066 0         0 my $name = PrintableTagID($tag);
9067 0         0 $tagInfo = {
9068             Name => "Unknown_$name",
9069             Description => "Unknown $name",
9070             Unknown => 1,
9071             },
9072             AddTagToTable($tagTablePtr, $tag, $tagInfo);
9073             }
9074 0 0       0 $val = '0x' . unpack('H*', $val) unless $$tagInfo{Format};
9075 0         0 $et->HandleTag($tagTablePtr, $tag, $val,
9076             DataPt => $dataPt,
9077             DataPos => $dataPos,
9078             Start => $pos + 4,
9079             Size => 4,
9080             );
9081             }
9082 0         0 return 1;
9083             }
9084              
9085             #------------------------------------------------------------------------------
9086             # Process iTunes Encoding Params (ref PH)
9087             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9088             # Returns: 1 on success
9089             sub ProcessEncodingParams($$$)
9090             {
9091 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
9092 0         0 my $dataPt = $$dirInfo{DataPt};
9093 0         0 my $dirLen = length $$dataPt;
9094 0         0 my $pos;
9095 0         0 $et->VerboseDir('Encoding Params', $dirLen / 8);
9096 0         0 for ($pos = 0; $pos + 8 <= $dirLen; $pos += 8) {
9097 0         0 my ($tag, $val) = unpack("x${pos}a4N", $$dataPt);
9098 0         0 $et->HandleTag($tagTablePtr, $tag, $val);
9099             }
9100 0         0 return 1;
9101             }
9102              
9103             #------------------------------------------------------------------------------
9104             # Read Meta Keys and add tags to ItemList table ('mdta' handler) (ref PH)
9105             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9106             # Returns: 1 on success
9107             sub ProcessKeys($$$)
9108             {
9109 9     9 0 26 local $_;
9110 9         26 my ($et, $dirInfo, $tagTablePtr) = @_;
9111 9         28 my $dataPt = $$dirInfo{DataPt};
9112 9         27 my $dirLen = length $$dataPt;
9113 9         19 my $out;
9114 9 50       39 if ($et->Options('Verbose')) {
9115 0         0 $et->VerboseDir('Keys');
9116 0         0 $out = $et->Options('TextOut');
9117             }
9118 9         20 my $pos = 8;
9119 9         23 my $index = 1;
9120 9         30 ++$$et{KeysCount}; # increment key count for this directory
9121 9         30 my $itemList = GetTagTable('Image::ExifTool::QuickTime::ItemList');
9122 9         28 my $userData = GetTagTable('Image::ExifTool::QuickTime::UserData');
9123 9         100 while ($pos < $dirLen - 4) {
9124 22         88 my $len = unpack("x${pos}N", $$dataPt);
9125 22 50 33     116 last if $len < 8 or $pos + $len > $dirLen;
9126 22         57 delete $$tagTablePtr{$index};
9127 22         62 my $ns = substr($$dataPt, $pos + 4, 4);
9128 22         57 my $tag = substr($$dataPt, $pos + 8, $len - 8);
9129 22         50 $tag =~ s/\0.*//s; # truncate at null
9130 22         41 my $full = $tag;
9131 22 50       122 $tag =~ s/^com\.(apple\.quicktime\.)?// if $ns eq 'mdta'; # remove apple quicktime domain
9132 22 50       68 $tag = "Tag_$ns" unless $tag;
9133 22         58 my $short = $tag;
9134 22         42 my $tagInfo;
9135 22         39 for (;;) {
9136 22 50       70 $tagInfo = $et->GetTagInfo($tagTablePtr, $tag) and last;
9137             # also try ItemList and UserData tables
9138 0 0       0 $tagInfo = $et->GetTagInfo($itemList, $tag) and last;
9139 0 0       0 $tagInfo = $et->GetTagInfo($userData, $tag) and last;
9140             # (I have some samples where the tag is a reversed ItemList or UserData tag ID)
9141 0 0       0 if ($tag =~ /^\w{3}\xa9$/) {
9142 0         0 $tag = pack('N', unpack('V', $tag));
9143 0 0       0 $tagInfo = $et->GetTagInfo($itemList, $tag) and last;
9144 0         0 $tagInfo = $et->GetTagInfo($userData, $tag);
9145 0         0 last;
9146             }
9147 0 0       0 if ($tag eq $full) {
9148 0         0 $tag = $short;
9149 0         0 last;
9150             }
9151 0         0 $tag = $full;
9152             }
9153 22         48 my ($newInfo, $msg);
9154 22 50 0     65 if ($tagInfo) {
    0          
9155             # copy tag information into new Keys tag
9156             $newInfo = {
9157             Name => $$tagInfo{Name},
9158             Format => $$tagInfo{Format},
9159             ValueConv => $$tagInfo{ValueConv},
9160             ValueConvInv => $$tagInfo{ValueConvInv},
9161             PrintConv => $$tagInfo{PrintConv},
9162             PrintConvInv => $$tagInfo{PrintConvInv},
9163             Writable => defined $$tagInfo{Writable} ? $$tagInfo{Writable} : 1,
9164             SubDirectory => $$tagInfo{SubDirectory},
9165 22 50       223 };
9166 22         53 my $groups = $$tagInfo{Groups};
9167 22 100       113 $$newInfo{Groups} = $groups ? { %$groups } : { };
9168 22   66     181 $$newInfo{Groups}{$_} or $$newInfo{Groups}{$_} = $$tagTablePtr{GROUPS}{$_} foreach 0..2;
9169 22         55 $$newInfo{Groups}{1} = 'Keys';
9170             } elsif ($tag =~ /^[-\w. ]+$/ or $tag =~ /\w{4}/) {
9171             # create info for tags with reasonable id's
9172 0         0 my $name = ucfirst $tag;
9173 0         0 $name =~ tr/-0-9a-zA-Z_. //dc;
9174 0         0 $name =~ s/[. ]+(.?)/\U$1/g;
9175 0         0 $name =~ s/_([a-z])/_\U$1/g;
9176 0         0 $name =~ s/([a-z])_([A-Z])/$1$2/g;
9177 0 0       0 $name = "Tag_$name" if length $name < 2;
9178 0         0 $newInfo = { Name => $name, Groups => { 1 => 'Keys' } };
9179 0         0 $msg = ' (Unknown)';
9180             }
9181             # substitute this tag in the ItemList table with the given index
9182 22         67 my $id = $$et{KeysCount} . '.' . $index;
9183 22 100       74 if (ref $$itemList{$id} eq 'HASH') {
9184             # delete other languages too if they exist
9185 18         41 my $oldInfo = $$itemList{$id};
9186 18 50       56 if ($$oldInfo{OtherLang}) {
9187 0         0 delete $$itemList{$_} foreach @{$$oldInfo{OtherLang}};
  0         0  
9188             }
9189 18         172 delete $$itemList{$id};
9190             }
9191 22 50       59 if ($newInfo) {
9192 22         53 $$newInfo{KeysID} = $tag; # save original ID for use in family 7 group name
9193 22         96 AddTagToTable($itemList, $id, $newInfo);
9194 22 50       59 $msg or $msg = '';
9195 22 50       48 $out and print $out "$$et{INDENT}Added ItemList Tag $id = ($ns) $tag$msg\n";
9196             }
9197 22         46 $pos += $len;
9198 22         77 ++$index;
9199             }
9200 9         32 return 1;
9201             }
9202              
9203             #------------------------------------------------------------------------------
9204             # Process keys in MetaSampleDesc directory
9205             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9206             # Returns: 1 on success
9207             sub ProcessMetaKeys($$$)
9208             {
9209 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
9210             # save this information to decode timed metadata samples when ExtractEmbedded is used
9211 0 0       0 SaveMetaKeys($et, $dirInfo, $tagTablePtr) if $$et{OPTIONS}{ExtractEmbedded};
9212 0         0 return 1;
9213             }
9214              
9215             #------------------------------------------------------------------------------
9216             # Process a QuickTime atom
9217             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) optional tag table ref
9218             # Returns: 1 on success
9219             sub ProcessMOV($$;$)
9220             {
9221 383     383 0 684 local $_;
9222 383         837 my ($et, $dirInfo, $tagTablePtr) = @_;
9223 383         1246 my $raf = $$dirInfo{RAF};
9224 383         721 my $dataPt = $$dirInfo{DataPt};
9225 383         1259 my $verbose = $et->Options('Verbose');
9226 383         859 my $validate = $$et{OPTIONS}{Validate};
9227 383   100     1051 my $dataPos = $$dirInfo{Base} || 0;
9228 383   100     1080 my $dirID = $$dirInfo{DirID} || '';
9229 383         846 my $charsetQuickTime = $et->Options('CharsetQuickTime');
9230 383         1197 my ($buff, $tag, $size, $track, $isUserData, %triplet, $doDefaultLang, $index);
9231 383         0 my ($dirEnd, $unkOpt, %saveOptions, $atomCount);
9232              
9233 383         849 my $topLevel = not $$et{InQuickTime};
9234 383         667 $$et{InQuickTime} = 1;
9235 383 100       1043 $$et{HandlerType} = $$et{MetaFormat} = '' unless defined $$et{HandlerType};
9236              
9237 383 100       983 unless (defined $$et{KeysCount}) {
9238 20         59 $$et{KeysCount} = 0; # initialize ItemList key directory count
9239 20         48 $doDefaultLang = 1; # flag to generate default language tags
9240             }
9241             # more convenient to package data as a RandomAccess file
9242 383 100       857 unless ($raf) {
9243 363         1258 $raf = new File::RandomAccess($dataPt);
9244 363 50 100     1731 $dirEnd = $dataPos + $$dirInfo{DirLen} + ($$dirInfo{DirStart} || 0) if $$dirInfo{DirLen};
9245             }
9246             # skip leading bytes if necessary
9247 383 100       940 if ($$dirInfo{DirStart}) {
9248 94 50       356 $raf->Seek($$dirInfo{DirStart}, 1) or return 0;
9249 94         205 $dataPos += $$dirInfo{DirStart};
9250             }
9251             # read size/tag name atom header
9252 383 50       1038 $raf->Read($buff,8) == 8 or return 0;
9253 383         667 $dataPos += 8;
9254 383 100       835 if ($tagTablePtr) {
9255 363         1193 $isUserData = ($tagTablePtr eq \%Image::ExifTool::QuickTime::UserData);
9256             } else {
9257 20         86 $tagTablePtr = GetTagTable('Image::ExifTool::QuickTime::Main');
9258             }
9259 383         1510 ($size, $tag) = unpack('Na4', $buff);
9260 383 100       913 if ($dataPt) {
9261 363 50       856 $verbose and $et->VerboseDir($$dirInfo{DirName});
9262             } else {
9263             # check on file type if called with a RAF
9264 20 50       96 $$tagTablePtr{$tag} or return 0;
9265 20         41 my $fileType;
9266 20 100 66     138 if ($tag eq 'ftyp' and $size >= 12) {
9267             # read ftyp atom to see what type of file this is
9268 11 50       65 if ($raf->Read($buff, $size-8) == $size-8) {
9269 11         69 $raf->Seek(-($size-8), 1);
9270 11         55 my $type = substr($buff, 0, 4);
9271 11         51 $$et{save_ftyp} = $type;
9272             # see if we know the extension for this file type
9273 11 50 33     159 if ($ftypLookup{$type} and $ftypLookup{$type} =~ /\(\.(\w+)/) {
    0          
    0          
    0          
9274 11         65 $fileType = $1;
9275             # check compatible brands
9276             } elsif ($buff =~ /^.{8}(.{4})+(mp41|mp42|avc1)/s) {
9277 0         0 $fileType = 'MP4';
9278             } elsif ($buff =~ /^.{8}(.{4})+(f4v )/s) {
9279 0         0 $fileType = 'F4V';
9280             } elsif ($buff =~ /^.{8}(.{4})+(qt )/s) {
9281 0         0 $fileType = 'MOV';
9282             }
9283             }
9284 11 50       57 $fileType or $fileType = 'MP4'; # default to MP4
9285 11   50     112 $et->SetFileType($fileType, $mimeLookup{$fileType} || 'video/mp4');
9286             # temporarily set ExtractEmbedded option for CRX files
9287 11 100       79 $saveOptions{ExtractEmbedded} = $et->Options(ExtractEmbedded => 1) if $fileType eq 'CRX';
9288             } else {
9289 9         50 $et->SetFileType(); # MOV
9290             }
9291 20         111 SetByteOrder('MM');
9292             # have XMP take priority except for HEIC
9293 20 50 66     156 $$et{PRIORITY_DIR} = 'XMP' unless $fileType and $fileType eq 'HEIC';
9294             }
9295 383   50     1527 my $fast = $$et{OPTIONS}{FastScan} || 0;
9296 383 50       820 $$raf{NoBuffer} = 1 if $fast; # disable buffering in FastScan mode
9297              
9298 383         751 my $ee = $$et{OPTIONS}{ExtractEmbedded};
9299 383         739 my $md5 = $$et{ImageDataMD5};
9300 383 100 66     1223 if ($ee or $md5) {
9301 134         285 $unkOpt = $$et{OPTIONS}{Unknown};
9302 134         3630 require 'Image/ExifTool/QuickTimeStream.pl';
9303             }
9304 383 100       1008 if ($$tagTablePtr{VARS}) {
9305 22         68 $index = $$tagTablePtr{VARS}{START_INDEX};
9306 22         74 $atomCount = $$tagTablePtr{VARS}{ATOM_COUNT};
9307             }
9308 383         599 for (;;) {
9309 1253         2139 my ($eeTag, $ignore);
9310 1253 50 33     2975 last if defined $atomCount and --$atomCount < 0;
9311 1253 100       2424 if ($size < 8) {
9312 4 50       40 if ($size == 0) {
9313 0 0       0 if ($dataPt) {
9314             # a zero size isn't legal for contained atoms, but Canon uses it to
9315             # terminate the CNTH atom (eg. CanonEOS100D.mov), so tolerate it here
9316 0         0 my $pos = $raf->Tell() - 4;
9317 0         0 $raf->Seek(0,2);
9318 0         0 my $str = $$dirInfo{DirName} . ' with ' . ($raf->Tell() - $pos) . ' bytes';
9319 0         0 $et->VPrint(0,"$$et{INDENT}\[Terminator found in $str remaining]");
9320             } else {
9321 0         0 my $t = PrintableTagID($tag,2);
9322 0         0 $et->VPrint(0,"$$et{INDENT}Tag '${t}' extends to end of file");
9323 0 0       0 if ($$tagTablePtr{"$tag-size"}) {
9324 0         0 my $pos = $raf->Tell();
9325 0 0       0 unless ($fast) {
9326 0         0 $raf->Seek(0, 2);
9327 0         0 $et->HandleTag($tagTablePtr, "$tag-size", $raf->Tell() - $pos);
9328             }
9329 0 0       0 $et->HandleTag($tagTablePtr, "$tag-offset", $pos) if $$tagTablePtr{"$tag-offset"};
9330             }
9331             }
9332 0         0 last;
9333             }
9334 4 50       27 $size == 1 or $et->Warn('Invalid atom size'), last;
9335             # read extended atom size
9336 4 50       19 $raf->Read($buff, 8) == 8 or $et->Warn('Truncated atom header'), last;
9337 4         22 $dataPos += 8;
9338 4         45 my ($hi, $lo) = unpack('NN', $buff);
9339 4 50 33     35 if ($hi or $lo > 0x7fffffff) {
9340 0 0       0 if ($hi > 0x7fffffff) {
    0          
9341 0         0 $et->Warn('Invalid atom size');
9342 0         0 last;
9343             } elsif (not $et->Options('LargeFileSupport')) {
9344 0         0 $et->Warn('End of processing at large atom (LargeFileSupport not enabled)');
9345 0         0 last;
9346             }
9347             }
9348 4         20 $size = $hi * 4294967296 + $lo - 16;
9349 4 50       14 $size < 0 and $et->Warn('Invalid extended size'), last;
9350             } else {
9351 1249         1966 $size -= 8;
9352             }
9353 1253 50       2474 if ($validate) {
9354 0 0       0 $$et{ValidatePath} or $$et{ValidatePath} = { };
9355 0         0 my $path = join('-', @{$$et{PATH}}, $tag);
  0         0  
9356 0 0       0 $path =~ s/-Track-/-$$et{SET_GROUP1}-/ if $$et{SET_GROUP1};
9357 0 0 0     0 if ($$et{ValidatePath}{$path} and not $dupTagOK{$tag} and not $dupDirOK{$dirID}) {
      0        
9358 0         0 my $i = Get32u(\$tag,0);
9359 0 0       0 my $str = $i < 255 ? "index $i" : "tag '" . PrintableTagID($tag,2) . "'";
9360 0         0 $et->WarnOnce("Duplicate $str at " . join('-', @{$$et{PATH}}));
  0         0  
9361 0 0       0 $$et{ValidatePath} = { } if $path eq 'MOV-moov'; # avoid warnings for all contained dups
9362             }
9363 0         0 $$et{ValidatePath}{$path} = 1;
9364             }
9365 1253 50 66     2738 if ($isUserData and $$et{SET_GROUP1}) {
9366 0         0 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
9367             # add track name to UserData tags inside tracks
9368 0         0 $tag = $$et{SET_GROUP1} . $tag;
9369 0 0 0     0 if (not $$tagTablePtr{$tag} and $tagInfo) {
9370 0         0 my %newInfo = %$tagInfo;
9371 0         0 foreach ('Name', 'Description') {
9372 0 0       0 next unless $$tagInfo{$_};
9373 0         0 $newInfo{$_} = $$et{SET_GROUP1} . $$tagInfo{$_};
9374 0         0 $newInfo{$_} =~ s/^(Track\d+)Track/$1/; # remove duplicate "Track" in name
9375             }
9376 0         0 AddTagToTable($tagTablePtr, $tag, \%newInfo);
9377             }
9378             }
9379             # set flag to store additional information for ExtractEmbedded option
9380 1253         2693 my $handlerType = $$et{HandlerType};
9381 1253 100 100     7571 if ($eeBox{$handlerType} and $eeBox{$handlerType}{$tag}) {
    50 66        
    50 33        
      0        
      33        
      0        
9382 104 100 66     426 if ($ee or $md5) {
    50 33        
9383             # (there is another 'gps ' box with a track log that doesn't contain offsets)
9384 68 50 33     189 if ($tag ne 'gps ' or $eeBox{$handlerType}{$tag} eq $dirID) {
9385 68         103 $eeTag = 1;
9386 68         147 $$et{OPTIONS}{Unknown} = 1; # temporarily enable "Unknown" option
9387             }
9388             } elsif ($handlerType ne 'vide' and not $$et{OPTIONS}{Validate}) {
9389 0         0 EEWarn($et);
9390             }
9391             } elsif ($ee and $ee > 1 and $eeBox2{$handlerType} and $eeBox2{$handlerType}{$tag}) {
9392 0         0 $eeTag = 1;
9393 0         0 $$et{OPTIONS}{Unknown} = 1;
9394             } elsif ($md5 and $md5Box{$handlerType} and $md5Box{$handlerType}{$tag}) {
9395 0         0 $eeTag = 1;
9396 0         0 $$et{OPTIONS}{Unknown} = 1;
9397             }
9398 1253         3983 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
9399              
9400 1253 100       2710 $$et{OPTIONS}{Unknown} = $unkOpt if $eeTag; # restore Unknown option
9401              
9402             # allow numerical tag ID's
9403 1253 100       2702 unless ($tagInfo) {
9404 160         680 my $id = $$et{KeysCount} . '.' . unpack('N', $tag);
9405 160 100       558 if ($$tagTablePtr{$id}) {
9406 22         68 $tagInfo = $et->GetTagInfo($tagTablePtr, $id);
9407 22         56 $tag = $id;
9408             }
9409             }
9410             # generate tagInfo if Unknown option set
9411 1253 50 66     3641 if (not defined $tagInfo and ($$et{OPTIONS}{Unknown} or
      66        
9412             $verbose or $tag =~ /^\xa9/))
9413             {
9414 13         50 my $name = PrintableTagID($tag,1);
9415 13 50       55 if ($name =~ /^xa9(.*)/) {
9416 0         0 $tagInfo = {
9417             Name => "UserData_$1",
9418             Description => "User Data $1",
9419             };
9420             } else {
9421 13         120 $tagInfo = {
9422             Name => "Unknown_$name",
9423             Description => "Unknown $name",
9424             %unknownInfo,
9425             };
9426             }
9427 13         68 AddTagToTable($tagTablePtr, $tag, $tagInfo);
9428             }
9429             # save required tag sizes
9430 1253 100       3753 if ($$tagTablePtr{"$tag-size"}) {
9431 23         143 $et->HandleTag($tagTablePtr, "$tag-size", $size);
9432 23 50       259 $et->HandleTag($tagTablePtr, "$tag-offset", $raf->Tell()) if $$tagTablePtr{"$tag-offset"};
9433             }
9434             # stop processing at mdat/idat if -fast2 is used
9435 1253 0 0     2613 last if $fast > 1 and ($tag eq 'mdat' or $tag eq 'idat');
      33        
9436             # load values only if associated with a tag (or verbose) and not too big
9437 1253 50       2781 if ($size > 0x2000000) { # start to get worried above 32 MB
9438             # check for RIFF trailer (written by Auto-Vox dashcam)
9439 0 0       0 if ($buff =~ /^(gpsa|gps0|gsen|gsea)...\0/s) { # (yet seen only gpsa as first record)
9440 0         0 $et->VPrint(0, "Found RIFF trailer");
9441 0 0       0 if ($et->Options('ExtractEmbedded')) {
9442 0 0       0 $raf->Seek(-8, 1) or last; # seek back to start of trailer
9443 0         0 my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
9444 0         0 ProcessRIFFTrailer($et, { RAF => $raf }, $tbl);
9445             } else {
9446 0         0 EEWarn($et);
9447             }
9448 0         0 last;
9449             }
9450 0         0 $ignore = 1;
9451 0 0 0     0 if ($tagInfo and not $$tagInfo{Unknown} and not $eeTag) {
      0        
9452 0         0 my $t = PrintableTagID($tag,2);
9453 0 0       0 if ($size > 0x8000000) {
9454 0         0 $et->Warn("Skipping '${t}' atom > 128 MB", 1);
9455             } else {
9456 0 0       0 $et->Warn("Skipping '${t}' atom > 32 MB", 2) or $ignore = 0;
9457             }
9458             }
9459             }
9460 1253 100 66     4217 if (defined $tagInfo and not $ignore) {
9461             # set document number for this item property if necessary
9462 1165 100       2754 if ($$et{IsItemProperty}) {
9463 12         28 my $items = $$et{ItemInfo};
9464 12         21 my ($id, $prop, $docNum, $lowest);
9465 12   50     36 my $primary = $$et{PrimaryItem} || 0;
9466 12         39 ItemID: foreach $id (keys %$items) {
9467 36 100       78 next unless $$items{$id}{Association};
9468 22         36 my $item = $$items{$id};
9469 22         34 foreach $prop (@{$$item{Association}}) {
  22         49  
9470 41 100       87 next unless $prop == $index;
9471 12 100 0     88 if ($id == $primary or (not $dontInherit{$tag} and
    100 33        
    50 66        
      33        
9472             (not $$item{RefersTo} or $$item{RefersTo}{$primary})))
9473             {
9474             # this is associated with the primary item or an item describing
9475             # the primary item, so consider this part of the main document
9476 6         13 undef $docNum;
9477 6         10 undef $lowest;
9478 6         23 last ItemID;
9479             } elsif ($$item{DocNum}) {
9480             # this property is already associated with an item that has
9481             # an ExifTool document number, so use the lowest associated DocNum
9482 3 50 33     19 $docNum = $$item{DocNum} if not defined $docNum or $docNum > $$item{DocNum};
9483             } elsif (not defined $lowest or $lowest > $id) {
9484             # keep track of the lowest associated item ID
9485 3         8 $lowest = $id;
9486             }
9487             }
9488             }
9489 12 100 100     51 if (not defined $docNum and defined $lowest) {
9490             # this is the first time we've seen metadata from this item,
9491             # so use a new document number
9492 3         9 $docNum = ++$$et{DOC_COUNT};
9493 3         8 $$items{$lowest}{DocNum} = $docNum;
9494             }
9495 12         26 $$et{DOC_NUM} = $docNum;
9496             }
9497 1165         1777 my $val;
9498 1165         3080 my $missing = $size - $raf->Read($val, $size);
9499 1165 50       2685 if ($missing) {
9500 0         0 my $t = PrintableTagID($tag,2);
9501 0         0 $et->Warn("Truncated '${t}' data (missing $missing bytes)");
9502 0         0 last;
9503             }
9504             # use value to get tag info if necessary
9505 1165 100       2448 $tagInfo or $tagInfo = $et->GetTagInfo($tagTablePtr, $tag, \$val);
9506 1165   66     3555 my $hasData = ($$dirInfo{HasData} and $val =~ /\0...data\0/s);
9507 1165 50 33     2731 if ($verbose and not $hasData) {
9508 0         0 my $tval;
9509 0 0 0     0 if ($tagInfo and $$tagInfo{Format}) {
9510 0         0 $tval = ReadValue(\$val, 0, $$tagInfo{Format}, $$tagInfo{Count}, length($val));
9511             }
9512             $et->VerboseInfo($tag, $tagInfo,
9513             Value => $tval,
9514             DataPt => \$val,
9515             DataPos => $dataPos,
9516             Size => $size,
9517             Format => $tagInfo ? $$tagInfo{Format} : undef,
9518 0 0       0 Index => $index,
9519             );
9520             # print iref item ID numbers
9521 0 0       0 if ($dirID eq 'iref') {
9522 0         0 my ($id, $count, @to, $i);
9523 0 0       0 if ($$et{ItemRefVersion}) {
9524 0 0       0 ($id, $count, @to) = unpack('NnN*', $val) if length $val >= 10;
9525             } else {
9526 0 0       0 ($id, $count, @to) = unpack('nnn*', $val) if length $val >= 6;
9527             }
9528 0 0       0 defined $id or $id = '', $count = 0;
9529 0 0       0 $id .= " (wrong count: $count)" if $count != @to;
9530             # convert sequential numbers to a range
9531 0         0 for ($i=1; $i<@to; ) {
9532 0 0 0     0 $to[$i-1] =~ /(\d+)$/ and $to[$i] == $1 + 1 or ++$i, next;
9533 0         0 $to[$i-1] =~ s/(-.*)?$/-$to[$i]/;
9534 0         0 splice @to, $i, 1;
9535             }
9536 0         0 $et->VPrint(1, "$$et{INDENT} Item $id refers to: ",join(',',@to),"\n");
9537             }
9538             }
9539             # extract metadata from stream if ExtractEmbedded option is enabled
9540 1165 100       2128 if ($eeTag) {
9541 68         287 ParseTag($et, $tag, \$val);
9542             # forget this tag if we generated it only for ExtractEmbedded
9543 68 100 66     396 undef $tagInfo if $tagInfo and $$tagInfo{Unknown} and not $unkOpt;
      100        
9544             }
9545              
9546             # handle iTunesInfo mean/name/data triplets
9547 1165 100 100     4195 if ($tagInfo and $$tagInfo{Triplet}) {
9548 9 100 66     49 if ($tag eq 'data' and $triplet{mean} and $triplet{name}) {
      33        
9549 3         9 $tag = $triplet{name};
9550             # add 'mean' to name unless it is 'com.apple.iTunes'
9551 3 50       15 $tag = $triplet{mean} . '/' . $tag unless $triplet{mean} eq 'com.apple.iTunes';
9552 3         12 $tagInfo = $et->GetTagInfo($tagTablePtr, $tag, \$val);
9553 3 50       13 unless ($tagInfo) {
9554 0         0 my $name = $triplet{name};
9555 0         0 my $desc = $name;
9556 0         0 $name =~ tr/-_a-zA-Z0-9//dc;
9557 0         0 $desc =~ tr/_/ /;
9558 0         0 $tagInfo = {
9559             Name => $name,
9560             Description => $desc,
9561             };
9562 0         0 $et->VPrint(0, $$et{INDENT}, "[adding QuickTime:$name]\n");
9563 0         0 AddTagToTable($tagTablePtr, $tag, $tagInfo);
9564             }
9565             # ignore 8-byte header
9566 3 50       17 $val = substr($val, 8) if length($val) >= 8;
9567 3 50 33     21 unless ($$tagInfo{Format} or $$tagInfo{SubDirectory}) {
9568             # extract as binary if it contains any non-ASCII or control characters
9569 3 50       18 if ($val =~ /[^\x20-\x7e]/) {
9570 0         0 my $buff = $val;
9571 0         0 $val = \$buff;
9572             }
9573             }
9574 3         10 $$tagInfo{List} = 1; # (allow any of these tags to have multiple data elements)
9575 3 50       11 $et->VerboseInfo($tag, $tagInfo, Value => $val) if $verbose;
9576             } else {
9577 6 50       30 $triplet{$tag} = substr($val,4) if length($val) > 4;
9578 6         13 undef $tagInfo; # don't store this tag
9579             }
9580             }
9581 1165 100       2253 if ($tagInfo) {
9582 1131         2235 my $subdir = $$tagInfo{SubDirectory};
9583 1131 100 66     3446 if ($subdir) {
    100          
    100          
9584 674   100     2156 my $start = $$subdir{Start} || 0;
9585 674         1310 my ($base, $dPos) = ($dataPos, 0);
9586 674 50       1413 if ($$subdir{Base}) {
9587 0         0 $dPos -= eval $$subdir{Base};
9588 0         0 $base -= $dPos;
9589             }
9590             my %dirInfo = (
9591             DataPt => \$val,
9592             DataLen => $size,
9593             DirStart => $start,
9594             DirLen => $size - $start,
9595             DirName => $$subdir{DirName} || $$tagInfo{Name},
9596             DirID => $tag,
9597             HasData => $$subdir{HasData},
9598             Multi => $$subdir{Multi},
9599             IgnoreProp => $$subdir{IgnoreProp}, # (XML hack)
9600 674   66     6597 DataPos => $dPos,
9601             Base => $base, # (needed for IsOffset tags in binary data)
9602             );
9603 674 50       1752 $dirInfo{BlockInfo} = $tagInfo if $$tagInfo{BlockExtract};
9604 674 100 66     1638 if ($$subdir{ByteOrder} and $$subdir{ByteOrder} =~ /^Little/) {
9605 6         25 SetByteOrder('II');
9606             }
9607 674         1339 my $oldGroup1 = $$et{SET_GROUP1};
9608 674 100 33     3600 if ($$tagInfo{SubDirectory} and $$tagInfo{SubDirectory}{TagTable} and
      66        
9609             $$tagInfo{SubDirectory}{TagTable} eq 'Image::ExifTool::QuickTime::Track')
9610             {
9611 38 100       126 $track or $track = 0;
9612 38         139 $$et{SET_GROUP1} = 'Track' . (++$track);
9613             }
9614 674         2101 my $subTable = GetTagTable($$subdir{TagTable});
9615 674         1313 my $proc = $$subdir{ProcessProc};
9616             # make ProcessMOV() the default processing procedure for subdirectories
9617 674 100 100     2945 $proc = \&ProcessMOV unless $proc or $$subTable{PROCESS_PROC};
9618 674 50       1861 if ($size > $start) {
9619             # delay processing of ipco box until after all other boxes
9620 674 100 66     1806 if ($tag eq 'ipco' and not $$et{IsItemProperty}) {
9621 3         14 $$et{ItemPropertyContainer} = [ \%dirInfo, $subTable, $proc ];
9622 3         21 $et->VPrint(0,"$$et{INDENT}\[Process ipco box later]");
9623             } else {
9624 671         2404 $et->ProcessDirectory(\%dirInfo, $subTable, $proc);
9625             }
9626             }
9627 674 100       2095 if ($tag eq 'stbl') {
    100          
9628             # process sample data when exiting SampleTable box if extracting embedded
9629 38 100 66     247 ProcessSamples($et) if $ee or $md5;
9630             } elsif ($tag eq 'minf') {
9631 38         127 $$et{HandlerType} = ''; # reset handler type at end of media info box
9632             }
9633 674         1385 $$et{SET_GROUP1} = $oldGroup1;
9634 674         1627 SetByteOrder('MM');
9635             } elsif ($hasData) {
9636             # handle atoms containing 'data' tags
9637             # (currently ignore contained atoms: 'itif', 'name', etc.)
9638 157         270 my $pos = 0;
9639 157         235 for (;;) {
9640 318 100       743 last if $pos + 16 > $size;
9641 161         743 my ($len, $type, $flags, $ctry, $lang) = unpack("x${pos}Na4Nnn", $val);
9642 161 50 33     624 last if $pos + $len > $size or not $len;
9643 161         267 my ($value, $langInfo, $oldDir);
9644 161         273 my $format = $$tagInfo{Format};
9645 161 50 33     534 if ($type eq 'data' and $len >= 16) {
9646 161         251 $pos += 16;
9647 161         234 $len -= 16;
9648 161         337 $value = substr($val, $pos, $len);
9649             # format flags (ref 12):
9650             # 0x0=binary, 0x1=UTF-8, 0x2=UTF-16, 0x3=ShiftJIS,
9651             # 0x4=UTF-8 0x5=UTF-16, 0xd=JPEG, 0xe=PNG,
9652             # 0x15=signed int, 0x16=unsigned int, 0x17=float,
9653             # 0x18=double, 0x1b=BMP, 0x1c='meta' atom
9654 161 100       427 if ($stringEncoding{$flags}) {
9655             # handle all string formats
9656 116         451 $value = $et->Decode($value, $stringEncoding{$flags});
9657             # (shouldn't be null terminated, but some software writes it anyway)
9658 116 50       360 $value =~ s/\0$// unless $$tagInfo{Binary};
9659             } else {
9660 45 100       188 if (not $format) {
    100          
9661 12         54 $format = QuickTimeFormat($flags, $len);
9662             } elsif ($format =~ /^int\d+([us])$/) {
9663             # adjust integer to available length (but not int64)
9664 15         86 my $fmt = { 1=>'int8', 2=>'int16', 4=>'int32' }->{$len};
9665 15 50       77 $format = $fmt . $1 if defined $fmt;
9666             }
9667 45 100       116 if ($format) {
    50          
9668 33         164 $value = ReadValue(\$value, 0, $format, $$tagInfo{Count}, $len);
9669             } elsif (not $$tagInfo{ValueConv}) {
9670             # make binary data a scalar reference unless a ValueConv exists
9671 12         23 my $buf = $value;
9672 12         30 $value = \$buf;
9673             }
9674             }
9675             }
9676 161 100 66     625 if ($ctry or $lang) {
9677 1         6 my $langCode = GetLangCode($lang, $ctry);
9678 1 50       4 if ($langCode) {
9679             # get tagInfo for other language
9680 1         6 $langInfo = GetLangInfoQT($et, $tagInfo, $langCode);
9681             # save other language tag ID's so we can delete later if necessary
9682 1 50       4 if ($langInfo) {
9683 1 50       5 $$tagInfo{OtherLang} or $$tagInfo{OtherLang} = [ ];
9684 1         3 push @{$$tagInfo{OtherLang}}, $$langInfo{TagID};
  1         4  
9685             }
9686             }
9687             }
9688 161 100       308 $langInfo or $langInfo = $tagInfo;
9689 161 50       515 my $str = $qtFlags{$flags} ? " ($qtFlags{$flags})" : '';
9690 161 0       303 $et->VerboseInfo($tag, $langInfo,
    50          
9691             Value => ref $value ? $$value : $value,
9692             DataPt => \$val,
9693             DataPos => $dataPos,
9694             Start => $pos,
9695             Size => $len,
9696             Format => $format,
9697             Index => $index,
9698             Extra => sprintf(", Type='${type}', Flags=0x%x%s, Lang=0x%.4x",$flags,$str,$lang),
9699             ) if $verbose;
9700             # use "Keys" in path instead of ItemList if this was defined by a Keys tag
9701 161   100     895 my $isKey = $$tagInfo{Groups} && $$tagInfo{Groups}{1} && $$tagInfo{Groups}{1} eq 'Keys';
9702 161 100       372 if ($isKey) {
9703 22         49 $oldDir = $$et{PATH}[-1];
9704 22         50 $$et{PATH}[-1] = 'Keys';
9705             }
9706 161 50       643 $et->FoundTag($langInfo, $value) if defined $value;
9707 161 100       416 $$et{PATH}[-1] = $oldDir if $isKey;
9708 161         291 $pos += $len;
9709             }
9710             } elsif ($tag =~ /^\xa9/ or $$tagInfo{IText}) {
9711             # parse international text to extract all languages
9712 49         95 my $pos = 0;
9713 49 50       121 if ($$tagInfo{Format}) {
9714 0         0 $et->FoundTag($tagInfo, ReadValue(\$val, 0, $$tagInfo{Format}, undef, length($val)));
9715 0         0 $pos = $size;
9716             }
9717 49         76 for (;;) {
9718 98         177 my ($len, $lang);
9719 98 100 66     288 if ($$tagInfo{IText} and $$tagInfo{IText} >= 6) {
9720 4 100       17 last if $pos + $$tagInfo{IText} > $size;
9721 2         6 $pos += $$tagInfo{IText} - 2;
9722 2         8 $lang = unpack("x${pos}n", $val);
9723 2         5 $pos += 2;
9724 2         4 $len = $size - $pos;
9725             } else {
9726 94 100       247 last if $pos + 4 > $size;
9727 47         165 ($len, $lang) = unpack("x${pos}nn", $val);
9728 47         90 $pos += 4;
9729             # according to the QuickTime spec (ref 12), $len should include
9730             # 4 bytes for length and type words, but nobody (including
9731             # Apple, Pentax and Kodak) seems to add these in, so try
9732             # to allow for either
9733 47 50       120 if ($pos + $len > $size) {
9734 0         0 $len -= 4;
9735 0 0 0     0 last if $pos + $len > $size or $len < 0;
9736             }
9737             }
9738             # ignore any empty entries (or null padding) after the first
9739 49 50 33     166 next if not $len and $pos;
9740 49         122 my $str = substr($val, $pos, $len);
9741 49         90 my ($langInfo, $enc);
9742 49 50 33     249 if (($lang < 0x400 or $lang == 0x7fff) and $str !~ /^\xfe\xff/) {
      33        
9743             # this is a Macintosh language code
9744             # a language code of 0 is Macintosh english, so treat as default
9745 49 50       100 if ($lang) {
9746 0 0       0 if ($lang == 0x7fff) {
9747             # technically, ISO 639-2 doesn't have a 2-character
9748             # equivalent for 'und', but use 'un' anyway
9749 0         0 $lang = 'un';
9750             } else {
9751             # use Font.pm to look up language string
9752 0         0 require Image::ExifTool::Font;
9753 0         0 $lang = $Image::ExifTool::Font::ttLang{Macintosh}{$lang};
9754             }
9755             } else {
9756             # for the default language code of 0x0000, use UTF-8 instead
9757             # of the CharsetQuickTime setting if obviously UTF8
9758 49 50       169 $enc = 'UTF8' if Image::ExifTool::IsUTF8(\$str) > 0;
9759             }
9760             # the spec says only "Macintosh text encoding", but
9761             # allow this to be configured by the user
9762 49 50       131 $enc = $charsetQuickTime unless $enc;
9763             } else {
9764             # convert language code to ASCII (ignore read-only bit)
9765 0         0 $lang = UnpackLang($lang);
9766             # may be either UTF-8 or UTF-16BE
9767 0 0       0 $enc = $str=~s/^\xfe\xff// ? 'UTF16' : 'UTF8';
9768             }
9769 49 100       138 unless ($$tagInfo{NoDecode}) {
9770 48         163 $str = $et->Decode($str, $enc);
9771 48         126 $str =~ s/\0+$//; # remove any trailing nulls (eg. 3gp tags)
9772             }
9773 49 100 100     163 if ($$tagInfo{IText} and $$tagInfo{IText} > 6) {
9774 1         4 my $n = $$tagInfo{IText} - 6;
9775             # add back extra bytes (eg. 'rtng' box)
9776 1         5 $str = substr($val, $pos-$n-2, $n) . $str;
9777             }
9778 49 50       104 $langInfo = GetLangInfoQT($et, $tagInfo, $lang) if $lang;
9779 49   33     256 $et->FoundTag($langInfo || $tagInfo, $str);
9780 49         112 $pos += $len;
9781             }
9782             } else {
9783 251         540 my $format = $$tagInfo{Format};
9784 251 100       524 if ($format) {
9785 68         397 $val = ReadValue(\$val, 0, $format, $$tagInfo{Count}, length($val));
9786             }
9787 251         437 my $oldBase;
9788 251 50       687 if ($$tagInfo{SetBase}) {
9789 0         0 $oldBase = $$et{BASE};
9790 0         0 $$et{BASE} = $dataPos;
9791             }
9792 251         859 my $key = $et->FoundTag($tagInfo, $val);
9793 251 50       666 $$et{BASE} = $oldBase if defined $oldBase;
9794             # decode if necessary (NOTE: must be done after RawConv)
9795 251 50 66     1857 if (defined $key and (not $format or $format =~ /^string/) and
      100        
      100        
      66        
      100        
      66        
      66        
9796             not $$tagInfo{Unknown} and not $$tagInfo{ValueConv} and
9797             not $$tagInfo{Binary} and defined $$et{VALUE}{$key} and not ref $val)
9798             {
9799 20         64 my $vp = \$$et{VALUE}{$key};
9800 20 50 66     209 if (not ref $$vp and length($$vp) <= 65536 and $$vp =~ /[\x80-\xff]/) {
      66        
9801             # the encoding of this is not specified, so use CharsetQuickTime
9802             # unless the string is valid UTF-8
9803 0 0       0 my $enc = Image::ExifTool::IsUTF8($vp) > 0 ? 'UTF8' : $charsetQuickTime;
9804 0         0 $$vp = $et->Decode($$vp, $enc);
9805             }
9806             }
9807             }
9808             }
9809             } else {
9810 88 50       214 $et->VerboseInfo($tag, $tagInfo,
9811             Size => $size,
9812             Extra => sprintf(' at offset 0x%.4x', $raf->Tell()),
9813             ) if $verbose;
9814 88 50 33     387 if ($size and (not $raf->Seek($size-1, 1) or $raf->Read($buff, 1) != 1)) {
      66        
9815 0         0 my $t = PrintableTagID($tag,2);
9816 0         0 $et->Warn("Truncated '${t}' data");
9817 0         0 last;
9818             }
9819             }
9820 1253         2227 $dataPos += $size + 8; # point to start of next atom data
9821 1253 100 100     4201 last if $dirEnd and $dataPos >= $dirEnd; # (note: ignores last value if 0 bytes)
9822 890 100       2721 $raf->Read($buff, 8) == 8 or last;
9823 870         3151 ($size, $tag) = unpack('Na4', $buff);
9824 870 100       2431 ++$index if defined $index;
9825             }
9826             # tweak file type based on track content ("iso*" and "dash" ftyp only)
9827 383 0 66     1095 if ($topLevel and $$et{FileType} and $$et{FileType} eq 'MP4' and
      33        
      33        
      0        
      0        
      0        
      0        
9828             $$et{save_ftyp} and $$et{HasHandler} and $$et{save_ftyp} =~ /^(iso|dash)/ and
9829             $$et{HasHandler}{soun} and not $$et{HasHandler}{vide})
9830             {
9831 0         0 $et->OverrideFileType('M4A', 'audio/mp4');
9832             }
9833             # fill in missing defaults for alternate language tags
9834             # (the first language is taken as the default)
9835 383 100 100     839 if ($doDefaultLang and $$et{QTLang}) {
9836 1         4 QTLang: foreach $tag (@{$$et{QTLang}}) {
  1         5  
9837 1 50       5 next unless defined $$et{VALUE}{$tag};
9838 1 50       6 my $langInfo = $$et{TAG_INFO}{$tag} or next;
9839 1 50       5 my $tagInfo = $$langInfo{SrcTagInfo} or next;
9840 1         3 my $infoHash = $$et{TAG_INFO};
9841 1         4 my $name = $$tagInfo{Name};
9842             # loop through all instances of this tag name and generate the default-language
9843             # version only if we don't already have a QuickTime tag with this name
9844 1         3 my ($i, $key);
9845 1         6 for ($i=0, $key=$name; $$infoHash{$key}; ++$i, $key="$name ($i)") {
9846 1 50       8 next QTLang if $et->GetGroup($key, 0) eq 'QuickTime';
9847             }
9848 0         0 $et->FoundTag($tagInfo, $$et{VALUE}{$tag});
9849             }
9850 1         4 delete $$et{QTLang};
9851             }
9852             # process item information now that we are done processing its 'meta' container
9853 383 100 100     1430 HandleItemInfo($et) if $topLevel or $dirID eq 'meta';
9854              
9855 383 100 100     1005 ScanMediaData($et) if $ee and $topLevel; # brute force scan for metadata embedded in media data
9856              
9857             # restore any changed options
9858 383         1011 $et->Options($_ => $saveOptions{$_}) foreach keys %saveOptions;
9859 383         1920 return 1;
9860             }
9861              
9862             #------------------------------------------------------------------------------
9863             # Process a QuickTime Image File
9864             # Inputs: 0) ExifTool object reference, 1) directory information reference
9865             # Returns: 1 on success
9866             sub ProcessQTIF($$)
9867             {
9868 0     0 0   my ($et, $dirInfo) = @_;
9869 0           my $table = GetTagTable('Image::ExifTool::QuickTime::ImageFile');
9870 0           return ProcessMOV($et, $dirInfo, $table);
9871             }
9872              
9873             1; # end
9874              
9875             __END__