File Coverage

blib/lib/Image/ExifTool/QuickTime.pm
Criterion Covered Total %
statement 640 986 64.9
branch 330 692 47.6
condition 172 368 46.7
subroutine 29 39 74.3
pod 0 32 0.0
total 1171 2117 55.3


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 29     29   4945 use strict;
  29         60  
  29         969  
45 29     29   140 use vars qw($VERSION $AUTOLOAD %stringEncoding);
  29         56  
  29         1575  
46 29     29   163 use Image::ExifTool qw(:DataAccess :Utils);
  29         59  
  29         5289  
47 29     29   2258 use Image::ExifTool::Exif;
  29         104  
  29         772  
48 29     29   1344 use Image::ExifTool::GPS;
  29         57  
  29         343856  
49              
50             $VERSION = '2.74';
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 ProcessRIFFTrailer($$$);
66             sub ProcessTTAD($$$);
67             sub ProcessNMEA($$$);
68             sub ProcessGPSLog($$$);
69             sub SaveMetaKeys($$$);
70             # ++^^^^^^^^^^^^++
71             sub ParseItemLocation($$);
72             sub ParseContentDescribes($$);
73             sub ParseItemInfoEntry($$);
74             sub ParseItemPropAssoc($$);
75             sub FixWrongFormat($);
76             sub GetMatrixStructure($$);
77             sub ConvertISO6709($);
78             sub ConvInvISO6709($);
79             sub ConvertChapterList($);
80             sub PrintChapter($);
81             sub PrintGPSCoordinates($);
82             sub PrintInvGPSCoordinates($);
83             sub UnpackLang($;$);
84             sub WriteKeys($$$);
85             sub WriteQuickTime($$$);
86             sub WriteMOV($$);
87             sub GetLangInfo($$);
88             sub CheckQTValue($$$);
89              
90             # MIME types for all entries in the ftypLookup with file extensions
91             # (defaults to 'video/mp4' if not found in this lookup)
92             my %mimeLookup = (
93             '3G2' => 'video/3gpp2',
94             '3GP' => 'video/3gpp',
95             AAX => 'audio/vnd.audible.aax',
96             DVB => 'video/vnd.dvb.file',
97             F4A => 'audio/mp4',
98             F4B => 'audio/mp4',
99             JP2 => 'image/jp2',
100             JPM => 'image/jpm',
101             JPX => 'image/jpx',
102             M4A => 'audio/mp4',
103             M4B => 'audio/mp4',
104             M4P => 'audio/mp4',
105             M4V => 'video/x-m4v',
106             MOV => 'video/quicktime',
107             MQV => 'video/quicktime',
108             HEIC => 'image/heic',
109             HEVC => 'image/heic-sequence',
110             HEICS=> 'image/heic-sequence',
111             HEIF => 'image/heif',
112             HEIFS=> 'image/heif-sequence',
113             AVIF => 'image/avif', #PH (NC)
114             CRX => 'video/x-canon-crx', # (will get overridden)
115             );
116              
117             # look up file type from ftyp atom type, with MIME type in comment if known
118             # (ref http://www.ftyps.com/)
119             my %ftypLookup = (
120             '3g2a' => '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-0 V1.0', # video/3gpp2
121             '3g2b' => '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-A V1.0.0', # video/3gpp2
122             '3g2c' => '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-B v1.0', # video/3gpp2
123             '3ge6' => '3GPP (.3GP) Release 6 MBMS Extended Presentations', # video/3gpp
124             '3ge7' => '3GPP (.3GP) Release 7 MBMS Extended Presentations', # video/3gpp
125             '3gg6' => '3GPP Release 6 General Profile', # video/3gpp
126             '3gp1' => '3GPP Media (.3GP) Release 1 (probably non-existent)', # video/3gpp
127             '3gp2' => '3GPP Media (.3GP) Release 2 (probably non-existent)', # video/3gpp
128             '3gp3' => '3GPP Media (.3GP) Release 3 (probably non-existent)', # video/3gpp
129             '3gp4' => '3GPP Media (.3GP) Release 4', # video/3gpp
130             '3gp5' => '3GPP Media (.3GP) Release 5', # video/3gpp
131             '3gp6' => '3GPP Media (.3GP) Release 6 Basic Profile', # video/3gpp
132             '3gp6' => '3GPP Media (.3GP) Release 6 Progressive Download', # video/3gpp
133             '3gp6' => '3GPP Media (.3GP) Release 6 Streaming Servers', # video/3gpp
134             '3gs7' => '3GPP Media (.3GP) Release 7 Streaming Servers', # video/3gpp
135             'aax ' => 'Audible Enhanced Audiobook (.AAX)', #PH
136             'avc1' => 'MP4 Base w/ AVC ext [ISO 14496-12:2005]', # video/mp4
137             'CAEP' => 'Canon Digital Camera',
138             'caqv' => 'Casio Digital Camera',
139             'CDes' => 'Convergent Design',
140             'da0a' => 'DMB MAF w/ MPEG Layer II aud, MOT slides, DLS, JPG/PNG/MNG images',
141             'da0b' => 'DMB MAF, extending DA0A, with 3GPP timed text, DID, TVA, REL, IPMP',
142             'da1a' => 'DMB MAF audio with ER-BSAC audio, JPG/PNG/MNG images',
143             'da1b' => 'DMB MAF, extending da1a, with 3GPP timed text, DID, TVA, REL, IPMP',
144             'da2a' => 'DMB MAF aud w/ HE-AAC v2 aud, MOT slides, DLS, JPG/PNG/MNG images',
145             'da2b' => 'DMB MAF, extending da2a, with 3GPP timed text, DID, TVA, REL, IPMP',
146             'da3a' => 'DMB MAF aud with HE-AAC aud, JPG/PNG/MNG images',
147             'da3b' => 'DMB MAF, extending da3a w/ BIFS, 3GPP timed text, DID, TVA, REL, IPMP',
148             'dmb1' => 'DMB MAF supporting all the components defined in the specification',
149             'dmpf' => 'Digital Media Project', # various
150             'drc1' => 'Dirac (wavelet compression), encapsulated in ISO base media (MP4)',
151             'dv1a' => 'DMB MAF vid w/ AVC vid, ER-BSAC aud, BIFS, JPG/PNG/MNG images, TS',
152             'dv1b' => 'DMB MAF, extending dv1a, with 3GPP timed text, DID, TVA, REL, IPMP',
153             'dv2a' => 'DMB MAF vid w/ AVC vid, HE-AAC v2 aud, BIFS, JPG/PNG/MNG images, TS',
154             'dv2b' => 'DMB MAF, extending dv2a, with 3GPP timed text, DID, TVA, REL, IPMP',
155             'dv3a' => 'DMB MAF vid w/ AVC vid, HE-AAC aud, BIFS, JPG/PNG/MNG images, TS',
156             'dv3b' => 'DMB MAF, extending dv3a, with 3GPP timed text, DID, TVA, REL, IPMP',
157             'dvr1' => 'DVB (.DVB) over RTP', # video/vnd.dvb.file
158             'dvt1' => 'DVB (.DVB) over MPEG-2 Transport Stream', # video/vnd.dvb.file
159             'F4A ' => 'Audio for Adobe Flash Player 9+ (.F4A)', # audio/mp4
160             'F4B ' => 'Audio Book for Adobe Flash Player 9+ (.F4B)', # audio/mp4
161             'F4P ' => 'Protected Video for Adobe Flash Player 9+ (.F4P)', # video/mp4
162             'F4V ' => 'Video for Adobe Flash Player 9+ (.F4V)', # video/mp4
163             'isc2' => 'ISMACryp 2.0 Encrypted File', # ?/enc-isoff-generic
164             'iso2' => 'MP4 Base Media v2 [ISO 14496-12:2005]', # video/mp4 (or audio)
165             'iso3' => 'MP4 Base Media v3', # video/mp4 (or audio)
166             'iso4' => 'MP4 Base Media v4', # video/mp4 (or audio)
167             'iso5' => 'MP4 Base Media v5', # video/mp4 (or audio)
168             'iso6' => 'MP4 Base Media v6', # video/mp4 (or audio)
169             'iso7' => 'MP4 Base Media v7', # video/mp4 (or audio)
170             'iso8' => 'MP4 Base Media v8', # video/mp4 (or audio)
171             'iso9' => 'MP4 Base Media v9', # video/mp4 (or audio)
172             'isom' => 'MP4 Base Media v1 [IS0 14496-12:2003]', # video/mp4 (or audio)
173             'JP2 ' => 'JPEG 2000 Image (.JP2) [ISO 15444-1 ?]', # image/jp2
174             'JP20' => 'Unknown, from GPAC samples (prob non-existent)',
175             'jpm ' => 'JPEG 2000 Compound Image (.JPM) [ISO 15444-6]', # image/jpm
176             'jpx ' => 'JPEG 2000 with extensions (.JPX) [ISO 15444-2]', # image/jpx
177             'KDDI' => '3GPP2 EZmovie for KDDI 3G cellphones', # video/3gpp2
178             #LCAG => (found in CompatibleBrands of Leica MOV videos)
179             'M4A ' => 'Apple iTunes AAC-LC (.M4A) Audio', # audio/x-m4a
180             'M4B ' => 'Apple iTunes AAC-LC (.M4B) Audio Book', # audio/mp4
181             'M4P ' => 'Apple iTunes AAC-LC (.M4P) AES Protected Audio', # audio/mp4
182             'M4V ' => 'Apple iTunes Video (.M4V) Video', # video/x-m4v
183             'M4VH' => 'Apple TV (.M4V)', # video/x-m4v
184             'M4VP' => 'Apple iPhone (.M4V)', # video/x-m4v
185             'mj2s' => 'Motion JPEG 2000 [ISO 15444-3] Simple Profile', # video/mj2
186             'mjp2' => 'Motion JPEG 2000 [ISO 15444-3] General Profile', # video/mj2
187             'mmp4' => 'MPEG-4/3GPP Mobile Profile (.MP4/3GP) (for NTT)', # video/mp4
188             'mp21' => 'MPEG-21 [ISO/IEC 21000-9]', # various
189             'mp41' => 'MP4 v1 [ISO 14496-1:ch13]', # video/mp4
190             'mp42' => 'MP4 v2 [ISO 14496-14]', # video/mp4
191             'mp71' => 'MP4 w/ MPEG-7 Metadata [per ISO 14496-12]', # various
192             'MPPI' => 'Photo Player, MAF [ISO/IEC 23000-3]', # various
193             'mqt ' => 'Sony / Mobile QuickTime (.MQV) US Patent 7,477,830 (Sony Corp)', # video/quicktime
194             'MSNV' => 'MPEG-4 (.MP4) for SonyPSP', # audio/mp4
195             'NDAS' => 'MP4 v2 [ISO 14496-14] Nero Digital AAC Audio', # audio/mp4
196             'NDSC' => 'MPEG-4 (.MP4) Nero Cinema Profile', # video/mp4
197             'NDSH' => 'MPEG-4 (.MP4) Nero HDTV Profile', # video/mp4
198             'NDSM' => 'MPEG-4 (.MP4) Nero Mobile Profile', # video/mp4
199             'NDSP' => 'MPEG-4 (.MP4) Nero Portable Profile', # video/mp4
200             'NDSS' => 'MPEG-4 (.MP4) Nero Standard Profile', # video/mp4
201             'NDXC' => 'H.264/MPEG-4 AVC (.MP4) Nero Cinema Profile', # video/mp4
202             'NDXH' => 'H.264/MPEG-4 AVC (.MP4) Nero HDTV Profile', # video/mp4
203             'NDXM' => 'H.264/MPEG-4 AVC (.MP4) Nero Mobile Profile', # video/mp4
204             'NDXP' => 'H.264/MPEG-4 AVC (.MP4) Nero Portable Profile', # video/mp4
205             'NDXS' => 'H.264/MPEG-4 AVC (.MP4) Nero Standard Profile', # video/mp4
206             'odcf' => 'OMA DCF DRM Format 2.0 (OMA-TS-DRM-DCF-V2_0-20060303-A)', # various
207             'opf2' => 'OMA PDCF DRM Format 2.1 (OMA-TS-DRM-DCF-V2_1-20070724-C)',
208             'opx2' => 'OMA PDCF DRM + XBS extensions (OMA-TS-DRM_XBS-V1_0-20070529-C)',
209             'pana' => 'Panasonic Digital Camera',
210             'qt ' => 'Apple QuickTime (.MOV/QT)', # video/quicktime
211             'ROSS' => 'Ross Video',
212             'sdv ' => 'SD Memory Card Video', # various?
213             'ssc1' => 'Samsung stereoscopic, single stream',
214             'ssc2' => 'Samsung stereoscopic, dual stream',
215             'XAVC' => 'Sony XAVC', #PH
216             'heic' => 'High Efficiency Image Format HEVC still image (.HEIC)', # image/heic
217             'hevc' => 'High Efficiency Image Format HEVC sequence (.HEICS)', # image/heic-sequence
218             'mif1' => 'High Efficiency Image Format still image (.HEIF)', # image/heif
219             'msf1' => 'High Efficiency Image Format sequence (.HEIFS)', # image/heif-sequence
220             'heix' => 'High Efficiency Image Format still image (.HEIF)', # image/heif (ref PH, Canon 1DXmkIII)
221             'avif' => 'AV1 Image File Format (.AVIF)', # image/avif
222             'crx ' => 'Canon Raw (.CRX)', #PH (CR3 or CRM; use Canon CompressorVersion to decide)
223             );
224              
225             # information for int32u date/time tags (time zero is Jan 1, 1904)
226             my %timeInfo = (
227             Notes => 'converted from UTC to local time if the QuickTimeUTC option is set',
228             Shift => 'Time',
229             Writable => 1,
230             Permanent => 1,
231             DelValue => 0,
232             # It is not uncommon for brain-dead software to use the wrong time zero, it should be
233             # Jan 1, 1904, so assume a time zero of Jan 1, 1970 if the date is before this
234             # Note: This value will be in UTC if generated by a system that is aware of the time zone
235             # (also note: this code is duplicated for the CreateDate tag)
236             RawConv => q{
237             my $offset = (66 * 365 + 17) * 24 * 3600;
238             return $val - $offset if $val >= $offset or $$self{OPTIONS}{QuickTimeUTC};
239             if ($val and not $$self{IsWriting}) {
240             $self->WarnOnce('Patched incorrect time zero for QuickTime date/time tag',1);
241             }
242             return $val;
243             },
244             RawConvInv => q{
245             if ($$self{FileType} eq 'CR3' and not $self->Options('QuickTimeUTC')) {
246             # convert to UTC
247             my $offset = (66 * 365 + 17) * 24 * 3600;
248             $val = ConvertUnixTime($val - $offset);
249             $val = GetUnixTime($val, 1) + $offset;
250             }
251             return $val;
252             },
253             # (all CR3 files store UTC times - PH)
254             ValueConv => 'ConvertUnixTime($val, $self->Options("QuickTimeUTC") || $$self{FileType} eq "CR3")',
255             ValueConvInv => q{
256             $val = GetUnixTime($val, $self->Options("QuickTimeUTC"));
257             return undef unless defined $val;
258             return $val + (66 * 365 + 17) * 24 * 3600;
259             },
260             PrintConv => '$self->ConvertDateTime($val)',
261             PrintConvInv => '$self->InverseDateTime($val)',
262             # (can't put Groups here because they aren't constant!)
263             );
264             # information for duration tags
265             my %durationInfo = (
266             ValueConv => '$$self{TimeScale} ? $val / $$self{TimeScale} : $val',
267             PrintConv => '$$self{TimeScale} ? ConvertDuration($val) : $val',
268             );
269             # handle unknown tags
270             my %unknownInfo = (
271             Unknown => 1,
272             ValueConv => '$val =~ /^([\x20-\x7e]*)\0*$/ ? $1 : \$val',
273             );
274              
275             # multi-language text with 6-byte header
276             my %langText = ( IText => 6 );
277              
278             # parsing for most of the 3gp udta language text boxes
279             my %langText3gp = (
280             Notes => 'used in 3gp videos',
281             Avoid => 1,
282             IText => 6,
283             );
284              
285             # 4-character Vendor ID codes (ref PH)
286             my %vendorID = (
287             appl => 'Apple',
288             fe20 => 'Olympus (fe20)', # (FE200)
289             FFMP => 'FFmpeg',
290             'GIC '=> 'General Imaging Co.',
291             kdak => 'Kodak',
292             KMPI => 'Konica-Minolta',
293             leic => 'Leica',
294             mino => 'Minolta',
295             niko => 'Nikon',
296             NIKO => 'Nikon',
297             olym => 'Olympus',
298             pana => 'Panasonic',
299             pent => 'Pentax',
300             pr01 => 'Olympus (pr01)', # (FE100,FE110,FE115)
301             sany => 'Sanyo',
302             'SMI '=> 'Sorenson Media Inc.',
303             ZORA => 'Zoran Corporation',
304             'AR.D'=> 'Parrot AR.Drone',
305             ' KD '=> 'Kodak', # (FZ201)
306             );
307              
308             # QuickTime data atom encodings for string types (ref 12)
309             %stringEncoding = (
310             1 => 'UTF8',
311             2 => 'UTF16',
312             3 => 'ShiftJIS',
313             4 => 'UTF8',
314             5 => 'UTF16',
315             );
316              
317             my %graphicsMode = (
318             # (ref http://homepage.mac.com/vanhoek/MovieGuts%20docs/64.html)
319             0x00 => 'srcCopy',
320             0x01 => 'srcOr',
321             0x02 => 'srcXor',
322             0x03 => 'srcBic',
323             0x04 => 'notSrcCopy',
324             0x05 => 'notSrcOr',
325             0x06 => 'notSrcXor',
326             0x07 => 'notSrcBic',
327             0x08 => 'patCopy',
328             0x09 => 'patOr',
329             0x0a => 'patXor',
330             0x0b => 'patBic',
331             0x0c => 'notPatCopy',
332             0x0d => 'notPatOr',
333             0x0e => 'notPatXor',
334             0x0f => 'notPatBic',
335             0x20 => 'blend',
336             0x21 => 'addPin',
337             0x22 => 'addOver',
338             0x23 => 'subPin',
339             0x24 => 'transparent',
340             0x25 => 'addMax',
341             0x26 => 'subOver',
342             0x27 => 'addMin',
343             0x31 => 'grayishTextOr',
344             0x32 => 'hilite',
345             0x40 => 'ditherCopy',
346             # the following ref ISO/IEC 15444-3
347             0x100 => 'Alpha',
348             0x101 => 'White Alpha',
349             0x102 => 'Pre-multiplied Black Alpha',
350             0x110 => 'Component Alpha',
351             );
352              
353             my %channelLabel = (
354             0xFFFFFFFF => 'Unknown',
355             0 => 'Unused',
356             100 => 'UseCoordinates',
357             1 => 'Left',
358             2 => 'Right',
359             3 => 'Center',
360             4 => 'LFEScreen',
361             5 => 'LeftSurround',
362             6 => 'RightSurround',
363             7 => 'LeftCenter',
364             8 => 'RightCenter',
365             9 => 'CenterSurround',
366             10 => 'LeftSurroundDirect',
367             11 => 'RightSurroundDirect',
368             12 => 'TopCenterSurround',
369             13 => 'VerticalHeightLeft',
370             14 => 'VerticalHeightCenter',
371             15 => 'VerticalHeightRight',
372             16 => 'TopBackLeft',
373             17 => 'TopBackCenter',
374             18 => 'TopBackRight',
375             33 => 'RearSurroundLeft',
376             34 => 'RearSurroundRight',
377             35 => 'LeftWide',
378             36 => 'RightWide',
379             37 => 'LFE2',
380             38 => 'LeftTotal',
381             39 => 'RightTotal',
382             40 => 'HearingImpaired',
383             41 => 'Narration',
384             42 => 'Mono',
385             43 => 'DialogCentricMix',
386             44 => 'CenterSurroundDirect',
387             45 => 'Haptic',
388             200 => 'Ambisonic_W',
389             201 => 'Ambisonic_X',
390             202 => 'Ambisonic_Y',
391             203 => 'Ambisonic_Z',
392             204 => 'MS_Mid',
393             205 => 'MS_Side',
394             206 => 'XY_X',
395             207 => 'XY_Y',
396             301 => 'HeadphonesLeft',
397             302 => 'HeadphonesRight',
398             304 => 'ClickTrack',
399             305 => 'ForeignLanguage',
400             400 => 'Discrete',
401             0x10000 => 'Discrete_0',
402             0x10001 => 'Discrete_1',
403             0x10002 => 'Discrete_2',
404             0x10003 => 'Discrete_3',
405             0x10004 => 'Discrete_4',
406             0x10005 => 'Discrete_5',
407             0x10006 => 'Discrete_6',
408             0x10007 => 'Discrete_7',
409             0x10008 => 'Discrete_8',
410             0x10009 => 'Discrete_9',
411             0x1000a => 'Discrete_10',
412             0x1000b => 'Discrete_11',
413             0x1000c => 'Discrete_12',
414             0x1000d => 'Discrete_13',
415             0x1000e => 'Discrete_14',
416             0x1000f => 'Discrete_15',
417             0x1ffff => 'Discrete_65535',
418             );
419              
420             # properties which don't get inherited from the parent
421             my %dontInherit = (
422             ispe => 1, # size of parent may be different
423             hvcC => 1, # (likely redundant)
424             );
425              
426             # tags that may be duplicated and directories that may contain duplicate tags
427             # (used only to avoid warnings when Validate-ing)
428             my %dupTagOK = ( mdat => 1, trak => 1, free => 1, infe => 1, sgpd => 1, dimg => 1, CCDT => 1,
429             sbgp => 1, csgm => 1, uuid => 1, cdsc => 1, maxr => 1, '----' => 1 );
430             my %dupDirOK = ( ipco => 1, '----' => 1 );
431              
432             # the usual atoms required to decode timed metadata with the ExtractEmbedded option
433             my %eeStd = ( stco => 'stbl', co64 => 'stbl', stsz => 'stbl', stz2 => 'stbl',
434             stsc => 'stbl', stts => 'stbl' );
435              
436             # boxes and their containers for the various handler types that we want to save
437             # when the ExtractEmbedded is enabled (currently only the 'gps ' container name is
438             # used, but others have been checked against all available sample files and may be
439             # useful in the future if the names are used for different boxes on other locations)
440             my %eeBox = (
441             # (note: vide is only processed if specific atoms exist in the VideoSampleDesc)
442             vide => { %eeStd,
443             JPEG => 'stsd',
444             },
445             text => { %eeStd },
446             meta => { %eeStd },
447             sbtl => { %eeStd },
448             data => { %eeStd },
449             camm => { %eeStd }, # (Insta360)
450             ctbx => { %eeStd }, # (GM cars)
451             '' => { 'gps ' => 'moov', 'GPS ' => 'main' }, # (no handler -- in top level 'moov' box, and main)
452             );
453             # boxes to save when ExtractEmbedded is set to 2 or higher
454             my %eeBox2 = (
455             vide => { avcC => 'stsd' }, # (parses H264 video stream)
456             );
457              
458             # QuickTime atoms
459             %Image::ExifTool::QuickTime::Main = (
460             PROCESS_PROC => \&ProcessMOV,
461             WRITE_PROC => \&WriteQuickTime, # (only needs to be defined for directories to process when writing)
462             GROUPS => { 2 => 'Video' },
463             meta => { # 'meta' is found here in my Sony ILCE-7S MP4 sample - PH
464             Name => 'Meta',
465             SubDirectory => {
466             TagTable => 'Image::ExifTool::QuickTime::Meta',
467             Start => 4, # skip 4-byte version number header
468             },
469             },
470             meco => { #ISO14496-12:2015
471             Name => 'OtherMeta',
472             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::OtherMeta' },
473             },
474             free => [
475             {
476             Name => 'KodakFree',
477             # (found in Kodak M5370 MP4 videos)
478             Condition => '$$valPt =~ /^\0\0\0.Seri/s',
479             SubDirectory => { TagTable => 'Image::ExifTool::Kodak::Free' },
480             },{
481             Name => 'Pittasoft',
482             # (Pittasoft Blackview dashcam MP4 videos)
483             Condition => '$$valPt =~ /^\0\0..(cprt|sttm|ptnm|ptrh|thum|gps |3gf )/s',
484             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Pittasoft' },
485             },{
486             Name => 'ThumbnailImage',
487             # (DJI Zenmuse XT2 thermal camera)
488             Groups => { 2 => 'Preview' },
489             Condition => '$$valPt =~ /^.{4}mdat\xff\xd8\xff/s',
490             RawConv => q{
491             my $len = unpack('N', $val);
492             return undef if $len <= 8 or $len > length($val);
493             return substr($val, 8, $len-8);
494             },
495             Binary => 1,
496             },{
497             Unknown => 1,
498             Binary => 1,
499             },
500             # (also Samsung WB750 uncompressed thumbnail data starting with "SDIC\0")
501             ],
502             # fre1 - 4 bytes: "june" (Kodak PixPro SP360)
503             frea => {
504             Name => 'Kodak_frea',
505             SubDirectory => { TagTable => 'Image::ExifTool::Kodak::frea' },
506             },
507             skip => [
508             {
509             Name => 'CanonSkip',
510             Condition => '$$valPt =~ /^\0.{3}(CNDB|CNCV|CNMN|CNFV|CNTH|CNDM)/s',
511             SubDirectory => { TagTable => 'Image::ExifTool::Canon::Skip' },
512             },
513             {
514             Name => 'PreviewImage', # (found in DuDuBell M1 dashcam MOV files)
515             Groups => { 2 => 'Preview' },
516             Condition => '$$valPt =~ /^.{12}\xff\xd8\xff/',
517             Binary => 1,
518             RawConv => q{
519             my $len = Get32u(\$val, 8);
520             return undef unless length($val) >= $len + 12;
521             return substr($val, 12, $len);
522             },
523             },
524             { Name => 'Skip', Unknown => 1, Binary => 1 },
525             ],
526             wide => { Unknown => 1, Binary => 1 },
527             ftyp => { #MP4
528             Name => 'FileType',
529             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::FileType' },
530             },
531             pnot => {
532             Name => 'Preview',
533             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Preview' },
534             },
535             PICT => {
536             Name => 'PreviewPICT',
537             Groups => { 2 => 'Preview' },
538             Binary => 1,
539             },
540             pict => { #8
541             Name => 'PreviewPICT',
542             Groups => { 2 => 'Preview' },
543             Binary => 1,
544             },
545             # (note that moov is present for an HEIF sequence)
546             moov => {
547             Name => 'Movie',
548             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Movie' },
549             },
550             moof => {
551             Name => 'MovieFragment',
552             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MovieFragment' },
553             },
554             # mfra - movie fragment random access: contains tfra (track fragment random access), and
555             # mfro (movie fragment random access offset) (ref 5)
556             mdat => { Name => 'MediaData', Unknown => 1, Binary => 1 },
557             'mdat-size' => {
558             Name => 'MediaDataSize',
559             Notes => q{
560             not a real tag ID, this tag represents the size of the 'mdat' data in bytes
561             and is used in the AvgBitrate calculation
562             },
563             },
564             'mdat-offset' => 'MediaDataOffset',
565             junk => { Unknown => 1, Binary => 1 }, #8
566             uuid => [
567             { #9 (MP4 files)
568             Name => 'XMP',
569             # *** this is where ExifTool writes XMP in MP4 videos (as per XMP spec) ***
570             Condition => '$$valPt=~/^\xbe\x7a\xcf\xcb\x97\xa9\x42\xe8\x9c\x71\x99\x94\x91\xe3\xaf\xac/',
571             WriteGroup => 'XMP', # (write main XMP tags here)
572             PreservePadding => 1,
573             SubDirectory => {
574             TagTable => 'Image::ExifTool::XMP::Main',
575             Start => 16,
576             },
577             },
578             { #11 (MP4 files)
579             Name => 'UUID-PROF',
580             Condition => '$$valPt=~/^PROF!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
581             SubDirectory => {
582             TagTable => 'Image::ExifTool::QuickTime::Profile',
583             Start => 24, # uid(16) + version(1) + flags(3) + count(4)
584             },
585             },
586             { #PH (Flip MP4 files)
587             Name => 'UUID-Flip',
588             Condition => '$$valPt=~/^\x4a\xb0\x3b\x0f\x61\x8d\x40\x75\x82\xb2\xd9\xfa\xce\xd3\x5f\xf5/',
589             SubDirectory => {
590             TagTable => 'Image::ExifTool::QuickTime::Flip',
591             Start => 16,
592             },
593             },
594             # "\x98\x7f\xa3\xdf\x2a\x85\x43\xc0\x8f\x8f\xd9\x7c\x47\x1e\x8e\xea" - unknown data in Flip videos
595             { #PH (Canon CR3)
596             Name => 'UUID-Canon2',
597             WriteLast => 1, # MUST come after mdat or DPP will drop mdat when writing!
598             Condition => '$$valPt=~/^\x21\x0f\x16\x87\x91\x49\x11\xe4\x81\x11\x00\x24\x21\x31\xfc\xe4/',
599             SubDirectory => {
600             TagTable => 'Image::ExifTool::Canon::uuid2',
601             Start => 16,
602             },
603             },
604             { # (ref https://github.com/JamesHeinrich/getID3/blob/master/getid3/module.audio-video.quicktime.php)
605             Name => 'SensorData', # sensor data for the 360Fly
606             Condition => '$$valPt=~/^\xef\xe1\x58\x9a\xbb\x77\x49\xef\x80\x95\x27\x75\x9e\xb1\xdc\x6f/ and $$self{OPTIONS}{ExtractEmbedded}',
607             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Tags360Fly' },
608             },
609             {
610             Name => 'SensorData',
611             Condition => '$$valPt=~/^\xef\xe1\x58\x9a\xbb\x77\x49\xef\x80\x95\x27\x75\x9e\xb1\xdc\x6f/',
612             Notes => 'raw 360Fly sensor data without ExtractEmbedded option',
613             RawConv => q{
614             $self->WarnOnce('Use the ExtractEmbedded option to decode timed SensorData',3);
615             return \$val;
616             },
617             },
618             { #PH (Canon CR3)
619             Name => 'PreviewImage',
620             Condition => '$$valPt=~/^\xea\xf4\x2b\x5e\x1c\x98\x4b\x88\xb9\xfb\xb7\xdc\x40\x6e\x4d\x16/',
621             Groups => { 2 => 'Preview' },
622             PreservePadding => 1,
623             # 0x00 - undef[16]: UUID
624             # 0x10 - int32u[2]: "0 1" (version and/or item count?)
625             # 0x18 - int32u: PRVW atom size
626             # 0x20 - int32u: 'PRVW'
627             # 0x30 - int32u: 0
628             # 0x34 - int16u: 1
629             # 0x36 - int16u: image width
630             # 0x38 - int16u: image height
631             # 0x3a - int16u: 1
632             # 0x3c - int32u: preview length
633             RawConv => '$val = substr($val, 0x30); $self->ValidateImage(\$val, $tag)',
634             },
635             { #8
636             Name => 'UUID-Unknown',
637             %unknownInfo,
638             },
639             ],
640             _htc => {
641             Name => 'HTCInfo',
642             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HTCInfo' },
643             },
644             udta => {
645             Name => 'UserData',
646             SubDirectory => { TagTable => 'Image::ExifTool::FLIR::UserData' },
647             },
648             thum => { #PH
649             Name => 'ThumbnailImage',
650             Groups => { 2 => 'Preview' },
651             Binary => 1,
652             },
653             'thm ' => { #PH (70mai A800)
654             Name => 'ThumbnailImage',
655             Groups => { 2 => 'Preview' },
656             Binary => 1,
657             },
658             ardt => { #PH
659             Name => 'ARDroneFile',
660             ValueConv => 'length($val) > 4 ? substr($val,4) : $val', # remove length
661             },
662             prrt => { #PH
663             Name => 'ARDroneTelemetry',
664             Notes => q{
665             telemetry information for each video frame: status1, status2, time, pitch,
666             roll, yaw, speed, altitude
667             },
668             ValueConv => q{
669             my $size = length $val;
670             return \$val if $size < 12 or not $$self{OPTIONS}{Binary};
671             my $len = Get16u(\$val, 2);
672             my $str = '';
673             SetByteOrder('II');
674             my $pos = 12;
675             while ($pos + $len <= $size) {
676             my $s1 = Get16u(\$val, $pos);
677             # s2: 7=take-off?, 3=moving, 4=hovering, 9=landing?, 2=landed
678             my $s2 = Get16u(\$val, $pos + 2);
679             $str .= "$s1 $s2";
680             my $num = int(($len-4)/4);
681             my ($i, $v);
682             for ($i=0; $i<$num; ++$i) {
683             my $pt = $pos + 4 + $i * 4;
684             if ($i > 0 && $i < 4) {
685             $v = GetFloat(\$val, $pt); # pitch/roll/yaw
686             } else {
687             $v = Get32u(\$val, $pt);
688             # convert time to sec, and speed(NC)/altitude to metres
689             $v /= 1000 if $i <= 5;
690             }
691             $str .= " $v";
692             }
693             $str .= "\n";
694             $pos += $len;
695             }
696             SetByteOrder('MM');
697             return \$str;
698             },
699             },
700             udat => { #PH (GPS NMEA-format log written by Datakam Player software)
701             Name => 'GPSLog',
702             Binary => 1, # (actually ASCII, but very lengthy)
703             Notes => 'parsed to extract GPS separately when ExtractEmbedded is used',
704             RawConv => q{
705             $val =~ s/\0+$//; # remove trailing nulls
706             if (length $val and $$self{OPTIONS}{ExtractEmbedded}) {
707             my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
708             Image::ExifTool::QuickTime::ProcessGPSLog($self, { DataPt => \$val }, $tagTbl);
709             }
710             return $val;
711             },
712             },
713             # meta - proprietary XML information written by some Flip cameras - PH
714             # beam - 16 bytes found in an iPhone video
715             IDIT => { #PH (written by DuDuBell M1, VSYS M6L dashcams)
716             Name => 'DateTimeOriginal',
717             Description => 'Date/Time Original',
718             Groups => { 2 => 'Time' },
719             Format => 'string', # (removes trailing "\0")
720             Shift => 'Time',
721             Writable => 1,
722             Permanent => 1,
723             DelValue => '0000-00-00T00:00:00+0000',
724             ValueConv => '$val=~tr/-/:/; $val',
725             ValueConvInv => '$val=~s/(\d+):(\d+):/$1-$2-/; $val',
726             PrintConv => '$self->ConvertDateTime($val)',
727             PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
728             },
729             gps0 => { #PH (DuDuBell M1, VSYS M6L)
730             Name => 'GPSTrack',
731             SubDirectory => {
732             TagTable => 'Image::ExifTool::QuickTime::Stream',
733             ProcessProc => \&Process_gps0,
734             },
735             },
736             gsen => { #PH (DuDuBell M1, VSYS M6L)
737             Name => 'GSensor',
738             SubDirectory => {
739             TagTable => 'Image::ExifTool::QuickTime::Stream',
740             ProcessProc => \&Process_gsen,
741             },
742             },
743             # gpsa - seen hex "01 20 00 00" (DuDuBell M1, VSYS M6L)
744             # gsea - 20 bytes hex "05 00's..." (DuDuBell M1) "05 08 02 01 ..." (VSYS M6L)
745             'GPS ' => { # GPS data written by 70mai dashcam (parsed in QuickTimeStream.pl)
746             Name => 'GPSDataList2',
747             Unknown => 1,
748             Binary => 1,
749             },
750             sefd => {
751             Name => 'SamsungTrailer',
752             SubDirectory => { TagTable => 'Image::ExifTool::Samsung::Trailer' },
753             },
754             # 'samn'? - seen in Vantrue N2S sample video
755             );
756              
757             # MPEG-4 'ftyp' atom
758             # (ref http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html)
759             %Image::ExifTool::QuickTime::FileType = (
760             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
761             GROUPS => { 2 => 'Video' },
762             FORMAT => 'int32u',
763             0 => {
764             Name => 'MajorBrand',
765             Format => 'undef[4]',
766             PrintConv => \%ftypLookup,
767             },
768             1 => {
769             Name => 'MinorVersion',
770             Format => 'undef[4]',
771             ValueConv => 'sprintf("%x.%x.%x", unpack("nCC", $val))',
772             },
773             2 => {
774             Name => 'CompatibleBrands',
775             Format => 'undef[$size-8]',
776             # ignore any entry with a null, and return others as a list
777             ValueConv => 'my @a=($val=~/.{4}/sg); @a=grep(!/\0/,@a); \@a',
778             },
779             );
780              
781             # proprietary HTC atom (HTC One MP4 video)
782             %Image::ExifTool::QuickTime::HTCInfo = (
783             PROCESS_PROC => \&ProcessMOV,
784             GROUPS => { 2 => 'Video' },
785             NOTES => 'Tags written by some HTC camera phones.',
786             slmt => {
787             Name => 'Unknown_slmt',
788             Unknown => 1,
789             Format => 'int32u', # (observed values: 4)
790             },
791             );
792              
793             # atoms used in QTIF files
794             %Image::ExifTool::QuickTime::ImageFile = (
795             PROCESS_PROC => \&ProcessMOV,
796             GROUPS => { 2 => 'Image' },
797             NOTES => 'Tags used in QTIF QuickTime Image Files.',
798             idsc => {
799             Name => 'ImageDescription',
800             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ImageDesc' },
801             },
802             idat => {
803             Name => 'ImageData',
804             Binary => 1,
805             },
806             iicc => {
807             Name => 'ICC_Profile',
808             SubDirectory => { TagTable => 'Image::ExifTool::ICC_Profile::Main' },
809             },
810             );
811              
812             # image description data block
813             %Image::ExifTool::QuickTime::ImageDesc = (
814             PROCESS_PROC => \&ProcessHybrid,
815             VARS => { ID_LABEL => 'ID/Index' },
816             GROUPS => { 2 => 'Image' },
817             FORMAT => 'int16u',
818             2 => {
819             Name => 'CompressorID',
820             Format => 'string[4]',
821             # not very useful since this isn't a complete list and name is given below
822             # # ref http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html
823             # PrintConv => {
824             # cvid => 'Cinepak',
825             # jpeg => 'JPEG',
826             # 'smc '=> 'Graphics',
827             # 'rle '=> 'Animation',
828             # rpza => 'Apple Video',
829             # kpcd => 'Kodak Photo CD',
830             # 'png '=> 'Portable Network Graphics',
831             # mjpa => 'Motion-JPEG (format A)',
832             # mjpb => 'Motion-JPEG (format B)',
833             # SVQ1 => 'Sorenson video, version 1',
834             # SVQ3 => 'Sorenson video, version 3',
835             # mp4v => 'MPEG-4 video',
836             # 'dvc '=> 'NTSC DV-25 video',
837             # dvcp => 'PAL DV-25 video',
838             # 'gif '=> 'Compuserve Graphics Interchange Format',
839             # h263 => 'H.263 video',
840             # tiff => 'Tagged Image File Format',
841             # 'raw '=> 'Uncompressed RGB',
842             # '2vuY'=> "Uncompressed Y'CbCr, 3x8-bit 4:2:2 (2vuY)",
843             # 'yuv2'=> "Uncompressed Y'CbCr, 3x8-bit 4:2:2 (yuv2)",
844             # v308 => "Uncompressed Y'CbCr, 8-bit 4:4:4",
845             # v408 => "Uncompressed Y'CbCr, 8-bit 4:4:4:4",
846             # v216 => "Uncompressed Y'CbCr, 10, 12, 14, or 16-bit 4:2:2",
847             # v410 => "Uncompressed Y'CbCr, 10-bit 4:4:4",
848             # v210 => "Uncompressed Y'CbCr, 10-bit 4:2:2",
849             # hvc1 => 'HEVC', #PH
850             # },
851             },
852             10 => {
853             Name => 'VendorID',
854             Format => 'string[4]',
855             RawConv => 'length $val ? $val : undef',
856             PrintConv => \%vendorID,
857             SeparateTable => 'VendorID',
858             },
859             # 14 - ("Quality" in QuickTime docs) ??
860             16 => 'SourceImageWidth',
861             17 => 'SourceImageHeight',
862             18 => { Name => 'XResolution', Format => 'fixed32u' },
863             20 => { Name => 'YResolution', Format => 'fixed32u' },
864             # 24 => 'FrameCount', # always 1 (what good is this?)
865             25 => {
866             Name => 'CompressorName',
867             Format => 'string[32]',
868             # (sometimes this is a Pascal string, and sometimes it is a C string)
869             RawConv => q{
870             $val=substr($val,1,ord($1)) if $val=~/^([\0-\x1f])/ and ord($1)
871             length $val ? $val : undef;
872             },
873             },
874             41 => 'BitDepth',
875             #
876             # Observed offsets for child atoms of various CompressorID types:
877             #
878             # CompressorID Offset Child atoms
879             # ----------- ------ ----------------
880             # avc1 86 avcC, btrt, colr, pasp, fiel, clap, svcC
881             # mp4v 86 esds, pasp
882             # s263 86 d263
883             #
884             btrt => {
885             Name => 'BitrateInfo',
886             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Bitrate' },
887             },
888             # Reference for fiel, colr, pasp, clap:
889             # https://developer.apple.com/library/mac/technotes/tn2162/_index.html#//apple_ref/doc/uid/DTS40013070-CH1-TNTAG9
890             fiel => {
891             Name => 'VideoFieldOrder',
892             ValueConv => 'join(" ", unpack("C*",$val))',
893             PrintConv => [{
894             1 => 'Progressive',
895             2 => '2:1 Interlaced',
896             }],
897             },
898             colr => {
899             Name => 'ColorRepresentation',
900             ValueConv => 'join(" ", substr($val,0,4), unpack("x4n*",$val))',
901             },
902             pasp => {
903             Name => 'PixelAspectRatio',
904             ValueConv => 'join(":", unpack("N*",$val))',
905             },
906             clap => {
907             Name => 'CleanAperture',
908             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::CleanAperture' },
909             },
910             avcC => {
911             # (see http://thompsonng.blogspot.ca/2010/11/mp4-file-format-part-2.html)
912             Name => 'AVCConfiguration',
913             Unknown => 1,
914             Binary => 1,
915             },
916             JPEG => { # (found in CR3 images; used as a flag to identify JpgFromRaw 'vide' stream)
917             Name => 'JPEGInfo',
918             # (4 bytes all zero)
919             Unknown => 1,
920             Binary => 1,
921             },
922             # hvcC - HEVC configuration
923             # svcC - 7 bytes: 00 00 00 00 ff e0 00
924             # esds - elementary stream descriptor
925             # d263
926             gama => { Name => 'Gamma', Format => 'fixed32u' },
927             # mjqt - default quantization table for MJPEG
928             # mjht - default Huffman table for MJPEG
929             # csgm ? (seen in hevc video)
930             CMP1 => { # Canon CR3
931             Name => 'CMP1',
932             SubDirectory => { TagTable => 'Image::ExifTool::Canon::CMP1' },
933             },
934             CDI1 => { # Canon CR3
935             Name => 'CDI1',
936             SubDirectory => {
937             TagTable => 'Image::ExifTool::Canon::CDI1',
938             Start => 4,
939             },
940             },
941             # JPEG - 4 bytes all 0 (Canon CR3)
942             # free - (Canon CR3)
943             #
944             # spherical video v2 stuff (untested)
945             #
946             st3d => {
947             Name => 'Stereoscopic3D',
948             Format => 'int8u',
949             ValueConv => '$val =~ s/.* //; $val', # (remove leading version/flags bytes?)
950             PrintConv => {
951             0 => 'Monoscopic',
952             1 => 'Stereoscopic Top-Bottom',
953             2 => 'Stereoscopic Left-Right',
954             3 => 'Stereoscopic Stereo-Custom', # (provisional in spec as of 2017-10-10)
955             },
956             },
957             sv3d => {
958             Name => 'SphericalVideo',
959             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::sv3d' },
960             },
961             );
962              
963             # 'sv3d' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
964             %Image::ExifTool::QuickTime::sv3d = (
965             PROCESS_PROC => \&ProcessMOV,
966             GROUPS => { 2 => 'Video' },
967             NOTES => q{
968             Tags defined by the Spherical Video V2 specification. See
969             L
970             for the specification.
971             },
972             svhd => {
973             Name => 'MetadataSource',
974             Format => 'undef',
975             ValueConv => '$val=~tr/\0//d; $val', # (remove version/flags? and terminator?)
976             },
977             proj => {
978             Name => 'Projection',
979             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::proj' },
980             },
981             );
982              
983             # 'proj' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
984             %Image::ExifTool::QuickTime::proj = (
985             PROCESS_PROC => \&ProcessMOV,
986             GROUPS => { 2 => 'Video' },
987             prhd => {
988             Name => 'ProjectionHeader',
989             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::prhd' },
990             },
991             cbmp => {
992             Name => 'CubemapProj',
993             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::cbmp' },
994             },
995             equi => {
996             Name => 'EquirectangularProj',
997             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::equi' },
998             },
999             # mshp - MeshProjection (P.I.T.A. to decode, for not much reward, see ref)
1000             );
1001              
1002             # 'prhd' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1003             %Image::ExifTool::QuickTime::prhd = (
1004             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1005             GROUPS => { 2 => 'Video' },
1006             FORMAT => 'fixed32s',
1007             # 0 - version (high 8 bits) / flags (low 24 bits)
1008             1 => 'PoseYawDegrees',
1009             2 => 'PosePitchDegrees',
1010             3 => 'PoseRollDegrees',
1011             );
1012              
1013             # 'cbmp' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1014             %Image::ExifTool::QuickTime::cbmp = (
1015             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1016             GROUPS => { 2 => 'Video' },
1017             FORMAT => 'int32u',
1018             # 0 - version (high 8 bits) / flags (low 24 bits)
1019             1 => 'Layout',
1020             2 => 'Padding',
1021             );
1022              
1023             # 'equi' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1024             %Image::ExifTool::QuickTime::equi = (
1025             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1026             GROUPS => { 2 => 'Video' },
1027             FORMAT => 'int32u', # (actually 0.32 fixed point)
1028             # 0 - version (high 8 bits) / flags (low 24 bits)
1029             1 => { Name => 'ProjectionBoundsTop', ValueConv => '$val / 4294967296' },
1030             2 => { Name => 'ProjectionBoundsBottom',ValueConv => '$val / 4294967296' },
1031             3 => { Name => 'ProjectionBoundsLeft', ValueConv => '$val / 4294967296' },
1032             4 => { Name => 'ProjectionBoundsRight', ValueConv => '$val / 4294967296' },
1033             );
1034              
1035             # 'btrt' atom information (ref http://lists.freedesktop.org/archives/gstreamer-commits/2011-October/054459.html)
1036             %Image::ExifTool::QuickTime::Bitrate = (
1037             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1038             GROUPS => { 2 => 'Video' },
1039             FORMAT => 'int32u',
1040             PRIORITY => 0, # often filled with zeros
1041             0 => 'BufferSize',
1042             1 => 'MaxBitrate',
1043             2 => 'AverageBitrate',
1044             );
1045              
1046             # 'clap' atom information (ref https://developer.apple.com/library/mac/technotes/tn2162/_index.html#//apple_ref/doc/uid/DTS40013070-CH1-TNTAG9)
1047             %Image::ExifTool::QuickTime::CleanAperture = (
1048             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1049             GROUPS => { 2 => 'Video' },
1050             FORMAT => 'rational64s',
1051             0 => 'CleanApertureWidth',
1052             1 => 'CleanApertureHeight',
1053             2 => 'CleanApertureOffsetX',
1054             3 => 'CleanApertureOffsetY',
1055             );
1056              
1057             # preview data block
1058             %Image::ExifTool::QuickTime::Preview = (
1059             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1060             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
1061             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
1062             GROUPS => { 2 => 'Image' },
1063             FORMAT => 'int16u',
1064             0 => {
1065             Name => 'PreviewDate',
1066             Format => 'int32u',
1067             Groups => { 2 => 'Time' },
1068             %timeInfo,
1069             },
1070             2 => 'PreviewVersion',
1071             3 => {
1072             Name => 'PreviewAtomType',
1073             Format => 'string[4]',
1074             },
1075             5 => 'PreviewAtomIndex',
1076             );
1077              
1078             # movie atoms
1079             %Image::ExifTool::QuickTime::Movie = (
1080             PROCESS_PROC => \&ProcessMOV,
1081             WRITE_PROC => \&WriteQuickTime,
1082             GROUPS => { 2 => 'Video' },
1083             mvhd => {
1084             Name => 'MovieHeader',
1085             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MovieHeader' },
1086             },
1087             trak => {
1088             Name => 'Track',
1089             CanCreate => 0, # don't create this atom
1090             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Track' },
1091             },
1092             udta => {
1093             Name => 'UserData',
1094             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::UserData' },
1095             },
1096             meta => { # 'meta' is found here in my EX-F1 MOV sample - PH
1097             Name => 'Meta',
1098             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1099             },
1100             iods => {
1101             Name => 'InitialObjectDescriptor',
1102             Flags => ['Binary','Unknown'],
1103             },
1104             uuid => [
1105             { #11 (MP4 files) (also found in QuickTime::Track)
1106             Name => 'UUID-USMT',
1107             Condition => '$$valPt=~/^USMT!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
1108             SubDirectory => {
1109             TagTable => 'Image::ExifTool::QuickTime::UserMedia',
1110             Start => 16,
1111             },
1112             },
1113             { #PH (Canon SX280)
1114             Name => 'UUID-Canon',
1115             Condition => '$$valPt=~/^\x85\xc0\xb6\x87\x82\x0f\x11\xe0\x81\x11\xf4\xce\x46\x2b\x6a\x48/',
1116             SubDirectory => {
1117             TagTable => 'Image::ExifTool::Canon::uuid',
1118             Start => 16,
1119             },
1120             },
1121             {
1122             Name => 'UUID-Unknown',
1123             %unknownInfo,
1124             },
1125             ],
1126             cmov => {
1127             Name => 'CompressedMovie',
1128             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::CMovie' },
1129             },
1130             htka => { # (written by HTC One M8 in slow-motion 1280x720 video - PH)
1131             Name => 'HTCTrack',
1132             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Track' },
1133             },
1134             'gps ' => { # GPS data written by Novatek cameras (parsed in QuickTimeStream.pl)
1135             Name => 'GPSDataList',
1136             Unknown => 1,
1137             Binary => 1,
1138             },
1139             meco => { #ISO14496-12:2015
1140             Name => 'OtherMeta',
1141             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::OtherMeta' },
1142             },
1143             # prfl - Profile (ref 12)
1144             # clip - clipping --> contains crgn (clip region) (ref 12)
1145             # mvex - movie extends --> contains mehd (movie extends header), trex (track extends) (ref 14)
1146             # ICAT - 4 bytes: "6350" (Nikon CoolPix S6900), "6500" (Panasonic FT7)
1147             );
1148              
1149             # (ref CFFMediaFormat-2_1.pdf)
1150             %Image::ExifTool::QuickTime::MovieFragment = (
1151             PROCESS_PROC => \&ProcessMOV,
1152             WRITE_PROC => \&WriteQuickTime,
1153             GROUPS => { 2 => 'Video' },
1154             mfhd => {
1155             Name => 'MovieFragmentHeader',
1156             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MovieFragHdr' },
1157             },
1158             traf => {
1159             Name => 'TrackFragment',
1160             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackFragment' },
1161             },
1162             meta => { #ISO14496-12:2015
1163             Name => 'Meta',
1164             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1165             },
1166             );
1167              
1168             # (ref CFFMediaFormat-2_1.pdf)
1169             %Image::ExifTool::QuickTime::MovieFragHdr = (
1170             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1171             GROUPS => { 2 => 'Video' },
1172             FORMAT => 'int32u',
1173             1 => 'MovieFragmentSequence',
1174             );
1175              
1176             # (ref CFFMediaFormat-2_1.pdf)
1177             %Image::ExifTool::QuickTime::TrackFragment = (
1178             PROCESS_PROC => \&ProcessMOV,
1179             WRITE_PROC => \&WriteQuickTime,
1180             GROUPS => { 2 => 'Video' },
1181             meta => { #ISO14496-12:2015
1182             Name => 'Meta',
1183             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1184             },
1185             # tfhd - track fragment header
1186             # edts - edits --> contains elst (edit list) (ref PH)
1187             # tfdt - track fragment base media decode time
1188             # trik - trick play box
1189             # trun - track fragment run box
1190             # avcn - AVC NAL unit storage box
1191             # secn - sample encryption box
1192             # saio - sample auxiliary information offsets box
1193             # sbgp - sample to group box
1194             # sgpd - sample group description box
1195             # sdtp - independent and disposable samples (ref 5)
1196             # subs - sub-sample information (ref 5)
1197             );
1198              
1199             # movie header data block
1200             %Image::ExifTool::QuickTime::MovieHeader = (
1201             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1202             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
1203             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
1204             GROUPS => { 2 => 'Video' },
1205             FORMAT => 'int32u',
1206             DATAMEMBER => [ 0, 1, 2, 3, 4 ],
1207             0 => {
1208             Name => 'MovieHeaderVersion',
1209             Format => 'int8u',
1210             RawConv => '$$self{MovieHeaderVersion} = $val',
1211             },
1212             1 => {
1213             Name => 'CreateDate',
1214             Groups => { 2 => 'Time' },
1215             %timeInfo,
1216             RawConv => q{
1217             my $offset = (66 * 365 + 17) * 24 * 3600;
1218             if ($val >= $offset or $$self{OPTIONS}{QuickTimeUTC}) {
1219             $val -= $offset;
1220             } elsif ($val and not $$self{IsWriting}) {
1221             $self->WarnOnce('Patched incorrect time zero for QuickTime date/time tag',1);
1222             }
1223             return $$self{CreateDate} = $val;
1224             },
1225             # this is int64u if MovieHeaderVersion == 1 (ref 13)
1226             Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
1227             },
1228             2 => {
1229             Name => 'ModifyDate',
1230             Groups => { 2 => 'Time' },
1231             %timeInfo,
1232             # this is int64u if MovieHeaderVersion == 1 (ref 13)
1233             Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
1234             },
1235             3 => {
1236             Name => 'TimeScale',
1237             RawConv => '$$self{TimeScale} = $val',
1238             },
1239             4 => {
1240             Name => 'Duration',
1241             %durationInfo,
1242             # this is int64u if MovieHeaderVersion == 1 (ref 13)
1243             Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
1244             },
1245             5 => {
1246             Name => 'PreferredRate',
1247             ValueConv => '$val / 0x10000',
1248             },
1249             6 => {
1250             Name => 'PreferredVolume',
1251             Format => 'int16u',
1252             ValueConv => '$val / 256',
1253             PrintConv => 'sprintf("%.2f%%", $val * 100)',
1254             },
1255             9 => {
1256             Name => 'MatrixStructure',
1257             Format => 'fixed32s[9]',
1258             # (the right column is fixed 2.30 instead of 16.16)
1259             ValueConv => q{
1260             my @a = split ' ',$val;
1261             $_ /= 0x4000 foreach @a[2,5,8];
1262             return "@a";
1263             },
1264             },
1265             18 => { Name => 'PreviewTime', %durationInfo },
1266             19 => { Name => 'PreviewDuration', %durationInfo },
1267             20 => { Name => 'PosterTime', %durationInfo },
1268             21 => { Name => 'SelectionTime', %durationInfo },
1269             22 => { Name => 'SelectionDuration',%durationInfo },
1270             23 => { Name => 'CurrentTime', %durationInfo },
1271             24 => 'NextTrackID',
1272             );
1273              
1274             # track atoms
1275             %Image::ExifTool::QuickTime::Track = (
1276             PROCESS_PROC => \&ProcessMOV,
1277             WRITE_PROC => \&WriteQuickTime,
1278             GROUPS => { 1 => 'Track#', 2 => 'Video' },
1279             tkhd => {
1280             Name => 'TrackHeader',
1281             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackHeader' },
1282             },
1283             udta => {
1284             Name => 'UserData',
1285             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::UserData' },
1286             },
1287             mdia => { #MP4
1288             Name => 'Media',
1289             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Media' },
1290             },
1291             meta => { #PH (MOV)
1292             Name => 'Meta',
1293             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1294             },
1295             tref => {
1296             Name => 'TrackRef',
1297             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackRef' },
1298             },
1299             tapt => {
1300             Name => 'TrackAperture',
1301             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackAperture' },
1302             },
1303             uuid => [
1304             { #11 (MP4 files) (also found in QuickTime::Movie)
1305             Name => 'UUID-USMT',
1306             Condition => '$$valPt=~/^USMT!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
1307             SubDirectory => {
1308             TagTable => 'Image::ExifTool::QuickTime::UserMedia',
1309             Start => 16,
1310             },
1311             },
1312             { #https://github.com/google/spatial-media/blob/master/docs/spherical-video-rfc.md
1313             Name => 'SphericalVideoXML',
1314             Condition => '$$valPt=~/^\xff\xcc\x82\x63\xf8\x55\x4a\x93\x88\x14\x58\x7a\x02\x52\x1f\xdd/',
1315             WriteGroup => 'GSpherical', # write only GSpherical XMP tags here
1316             HandlerType => 'vide', # only write in video tracks
1317             SubDirectory => {
1318             TagTable => 'Image::ExifTool::XMP::Main',
1319             Start => 16,
1320             WriteProc => 'Image::ExifTool::XMP::WriteGSpherical',
1321             },
1322             },
1323             {
1324             Name => 'UUID-Unknown',
1325             %unknownInfo,
1326             },
1327             ],
1328             meco => { #ISO14492-12:2015 pg 83
1329             Name => 'OtherMeta',
1330             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::OtherMeta' },
1331             },
1332             # edts - edits --> contains elst (edit list)
1333             # clip - clipping --> contains crgn (clip region)
1334             # matt - track matt --> contains kmat (compressed matt)
1335             # load - track loading settings
1336             # imap - track input map --> contains ' in' --> contains ' ty', obid
1337             # prfl - Profile (ref 12)
1338             );
1339              
1340             # track header data block
1341             %Image::ExifTool::QuickTime::TrackHeader = (
1342             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1343             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
1344             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
1345             GROUPS => { 1 => 'Track#', 2 => 'Video' },
1346             FORMAT => 'int32u',
1347             DATAMEMBER => [ 0, 1, 2, 5, 7 ],
1348             0 => {
1349             Name => 'TrackHeaderVersion',
1350             Format => 'int8u',
1351             Priority => 0,
1352             RawConv => '$$self{TrackHeaderVersion} = $val',
1353             },
1354             1 => {
1355             Name => 'TrackCreateDate',
1356             Priority => 0,
1357             Groups => { 2 => 'Time' },
1358             %timeInfo,
1359             # this is int64u if TrackHeaderVersion == 1 (ref 13)
1360             Hook => '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
1361             },
1362             2 => {
1363             Name => 'TrackModifyDate',
1364             Priority => 0,
1365             Groups => { 2 => 'Time' },
1366             %timeInfo,
1367             # this is int64u if TrackHeaderVersion == 1 (ref 13)
1368             Hook => '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
1369             },
1370             3 => {
1371             Name => 'TrackID',
1372             Priority => 0,
1373             },
1374             5 => {
1375             Name => 'TrackDuration',
1376             Priority => 0,
1377             %durationInfo,
1378             # this is int64u if TrackHeaderVersion == 1 (ref 13)
1379             Hook => '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
1380             },
1381             7 => { # (used only for writing MatrixStructure)
1382             Name => 'ImageSizeLookahead',
1383             Hidden => 1,
1384             Format => 'int32u[14]',
1385             RawConv => '$$self{ImageSizeLookahead} = $val; undef',
1386             },
1387             8 => {
1388             Name => 'TrackLayer',
1389             Format => 'int16u',
1390             Priority => 0,
1391             },
1392             9 => {
1393             Name => 'TrackVolume',
1394             Format => 'int16u',
1395             Priority => 0,
1396             ValueConv => '$val / 256',
1397             PrintConv => 'sprintf("%.2f%%", $val * 100)',
1398             },
1399             10 => {
1400             Name => 'MatrixStructure',
1401             Format => 'fixed32s[9]',
1402             Notes => 'writable for the video track via the Composite Rotation tag',
1403             Writable => 1,
1404             Protected => 1,
1405             Permanent => 1,
1406             # only set rotation if image size is non-zero
1407             RawConvInv => \&GetMatrixStructure,
1408             # (the right column is fixed 2.30 instead of 16.16)
1409             ValueConv => q{
1410             my @a = split ' ',$val;
1411             $_ /= 0x4000 foreach @a[2,5,8];
1412             return "@a";
1413             },
1414             ValueConvInv => q{
1415             my @a = split ' ',$val;
1416             $_ *= 0x4000 foreach @a[2,5,8];
1417             return "@a";
1418             },
1419             },
1420             19 => {
1421             Name => 'ImageWidth',
1422             Priority => 0,
1423             RawConv => \&FixWrongFormat,
1424             },
1425             20 => {
1426             Name => 'ImageHeight',
1427             Priority => 0,
1428             RawConv => \&FixWrongFormat,
1429             },
1430             );
1431              
1432             # user data atoms
1433             %Image::ExifTool::QuickTime::UserData = (
1434             PROCESS_PROC => \&ProcessMOV,
1435             WRITE_PROC => \&WriteQuickTime,
1436             CHECK_PROC => \&CheckQTValue,
1437             GROUPS => { 1 => 'UserData', 2 => 'Video' },
1438             WRITABLE => 1,
1439             PREFERRED => 1, # (preferred over Keys tags when writing)
1440             FORMAT => 'string',
1441             WRITE_GROUP => 'UserData',
1442             LANG_INFO => \&GetLangInfo,
1443             NOTES => q{
1444             Tag ID's beginning with the copyright symbol (hex 0xa9) are multi-language
1445             text. Alternate language tags are accessed by adding a dash followed by a
1446             3-character ISO 639-2 language code to the tag name. ExifTool will extract
1447             any multi-language user data tags found, even if they aren't in this table.
1448             Note when creating new tags,
1449             L tags are
1450             preferred over these, so to create the tag when a same-named ItemList tag
1451             exists, either "UserData" must be specified (eg. C<-UserData:Artist=Monet>
1452             on the command line), or the PREFERRED level must be changed via
1453             L.
1454             },
1455             "\xa9cpy" => { Name => 'Copyright', Groups => { 2 => 'Author' } },
1456             "\xa9day" => {
1457             Name => 'ContentCreateDate',
1458             Groups => { 2 => 'Time' },
1459             Shift => 'Time',
1460             # handle values in the form "2010-02-12T13:27:14-0800" (written by Apple iPhone)
1461             ValueConv => q{
1462             require Image::ExifTool::XMP;
1463             $val = Image::ExifTool::XMP::ConvertXMPDate($val);
1464             $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
1465             return $val;
1466             },
1467             ValueConvInv => q{
1468             require Image::ExifTool::XMP;
1469             my $tmp = Image::ExifTool::XMP::FormatXMPDate($val);
1470             ($val = $tmp) =~ s/([-+]\d{2}):(\d{2})$/$1$2/ if defined $tmp; # remove time zone colon
1471             return $val;
1472             },
1473             PrintConv => '$self->ConvertDateTime($val)',
1474             PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
1475             },
1476             "\xa9ART" => 'Artist', #PH (iTunes 8.0.2)
1477             "\xa9alb" => 'Album', #PH (iTunes 8.0.2)
1478             "\xa9arg" => 'Arranger', #12
1479             "\xa9ark" => 'ArrangerKeywords', #12
1480             "\xa9cmt" => 'Comment', #PH (iTunes 8.0.2)
1481             "\xa9cok" => 'ComposerKeywords', #12
1482             "\xa9com" => 'Composer', #12
1483             "\xa9dir" => 'Director', #12
1484             "\xa9ed1" => 'Edit1',
1485             "\xa9ed2" => 'Edit2',
1486             "\xa9ed3" => 'Edit3',
1487             "\xa9ed4" => 'Edit4',
1488             "\xa9ed5" => 'Edit5',
1489             "\xa9ed6" => 'Edit6',
1490             "\xa9ed7" => 'Edit7',
1491             "\xa9ed8" => 'Edit8',
1492             "\xa9ed9" => 'Edit9',
1493             "\xa9fmt" => 'Format',
1494             "\xa9gen" => 'Genre', #PH (iTunes 8.0.2)
1495             "\xa9grp" => 'Grouping', #PH (NC)
1496             "\xa9inf" => 'Information',
1497             "\xa9isr" => 'ISRCCode', #12
1498             "\xa9lab" => 'RecordLabelName', #12
1499             "\xa9lal" => 'RecordLabelURL', #12
1500             "\xa9lyr" => 'Lyrics', #PH (NC)
1501             "\xa9mak" => 'Make', #12
1502             "\xa9mal" => 'MakerURL', #12
1503             "\xa9mod" => 'Model', #PH
1504             "\xa9nam" => 'Title', #12
1505             "\xa9pdk" => 'ProducerKeywords', #12
1506             "\xa9phg" => 'RecordingCopyright', #12
1507             "\xa9prd" => 'Producer',
1508             "\xa9prf" => 'Performers',
1509             "\xa9prk" => 'PerformerKeywords', #12
1510             "\xa9prl" => 'PerformerURL',
1511             "\xa9req" => 'Requirements',
1512             "\xa9snk" => 'SubtitleKeywords', #12
1513             "\xa9snm" => 'Subtitle', #12
1514             "\xa9src" => 'SourceCredits', #12
1515             "\xa9swf" => 'SongWriter', #12
1516             "\xa9swk" => 'SongWriterKeywords', #12
1517             "\xa9swr" => 'SoftwareVersion', #12
1518             "\xa9too" => 'Encoder', #PH (NC)
1519             "\xa9trk" => 'Track', #PH (NC)
1520             "\xa9wrt" => { Name => 'Composer', Avoid => 1 }, # ("\xa9com" is preferred in UserData)
1521             "\xa9xyz" => { #PH (iPhone 3GS)
1522             Name => 'GPSCoordinates',
1523             Groups => { 2 => 'Location' },
1524             ValueConv => \&ConvertISO6709,
1525             ValueConvInv => \&ConvInvISO6709,
1526             PrintConv => \&PrintGPSCoordinates,
1527             PrintConvInv => \&PrintInvGPSCoordinates,
1528             },
1529             # \xa9 tags written by DJI Phantom 3: (ref PH)
1530             "\xa9xsp" => 'SpeedX', #PH (guess)
1531             "\xa9ysp" => 'SpeedY', #PH (guess)
1532             "\xa9zsp" => 'SpeedZ', #PH (guess)
1533             "\xa9fpt" => 'Pitch', #PH
1534             "\xa9fyw" => 'Yaw', #PH
1535             "\xa9frl" => 'Roll', #PH
1536             "\xa9gpt" => 'CameraPitch', #PH
1537             "\xa9gyw" => 'CameraYaw', #PH
1538             "\xa9grl" => 'CameraRoll', #PH
1539             "\xa9enc" => 'EncoderID', #PH (forum9271)
1540             # and the following entries don't have the proper 4-byte header for \xa9 tags:
1541             "\xa9dji" => { Name => 'UserData_dji', Format => 'undef', Binary => 1, Unknown => 1, Hidden => 1 },
1542             "\xa9res" => { Name => 'UserData_res', Format => 'undef', Binary => 1, Unknown => 1, Hidden => 1 },
1543             "\xa9uid" => { Name => 'UserData_uid', Format => 'undef', Binary => 1, Unknown => 1, Hidden => 1 },
1544             "\xa9mdl" => {
1545             Name => 'Model',
1546             Notes => 'non-standard-format DJI tag',
1547             Format => 'string',
1548             Avoid => 1,
1549             },
1550             # end DJI tags
1551             name => 'Name',
1552             WLOC => {
1553             Name => 'WindowLocation',
1554             Format => 'int16u',
1555             },
1556             LOOP => {
1557             Name => 'LoopStyle',
1558             Format => 'int32u',
1559             PrintConv => {
1560             1 => 'Normal',
1561             2 => 'Palindromic',
1562             },
1563             },
1564             SelO => {
1565             Name => 'PlaySelection',
1566             Format => 'int8u',
1567             },
1568             AllF => {
1569             Name => 'PlayAllFrames',
1570             Format => 'int8u',
1571             },
1572             meta => {
1573             Name => 'Meta',
1574             SubDirectory => {
1575             TagTable => 'Image::ExifTool::QuickTime::Meta',
1576             Start => 4, # must skip 4-byte version number header
1577             },
1578             },
1579             'ptv '=> {
1580             Name => 'PrintToVideo',
1581             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Video' },
1582             },
1583             hnti => {
1584             Name => 'HintInfo',
1585             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HintInfo' },
1586             },
1587             hinf => {
1588             Name => 'HintTrackInfo',
1589             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HintTrackInfo' },
1590             },
1591             hinv => 'HintVersion', #PH (guess)
1592             XMP_ => { #PH (Adobe CS3 Bridge)
1593             Name => 'XMP',
1594             WriteGroup => 'XMP', # (write main tags here)
1595             # *** this is where ExifTool writes XMP in MOV videos (as per XMP spec) ***
1596             SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
1597             },
1598             # the following are 3gp tags, references:
1599             # http://atomicparsley.sourceforge.net
1600             # http://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_25/Docs/
1601             # (note that all %langText3gp tags are Avoid => 1)
1602             cprt => { Name => 'Copyright', %langText3gp, Groups => { 2 => 'Author' } },
1603             auth => { Name => 'Author', %langText3gp, Groups => { 2 => 'Author' } },
1604             titl => { Name => 'Title', %langText3gp },
1605             dscp => { Name => 'Description',%langText3gp },
1606             perf => { Name => 'Performer', %langText3gp },
1607             gnre => { Name => 'Genre', %langText3gp },
1608             albm => { Name => 'Album', %langText3gp },
1609             coll => { Name => 'CollectionName', %langText3gp }, #17
1610             rtng => {
1611             Name => 'Rating',
1612             Writable => 'undef',
1613             Avoid => 1,
1614             # (4-byte flags, 4-char entity, 4-char criteria, 2-byte lang, string)
1615             IText => 14, # (14 bytes before string)
1616             Notes => 'string in the form "Entity=XXXX Criteria=XXXX XXXXX", used in 3gp videos',
1617             ValueConv => '$val=~s/^(.{4})(.{4})/Entity=$1 Criteria=$2 /i; $val',
1618             ValueConvInv => '$val=~s/Entity=(.{4}) Criteria=(.{4}) ?/$1$2/i; $val',
1619             },
1620             clsf => {
1621             Name => 'Classification',
1622             Writable => 'undef',
1623             Avoid => 1,
1624             # (4-byte flags, 4-char entity, 2-byte index, 2-byte lang, string)
1625             IText => 12,
1626             Notes => 'string in the form "Entity=XXXX Index=### XXXXX", used in 3gp videos',
1627             ValueConv => '$val=~s/^(.{4})(.{2})/"Entity=$1 Index=".unpack("n",$2)." "/ie; $val',
1628             ValueConvInv => '$val=~s/Entity=(.{4}) Index=(\d+) ?/$1.pack("n",$2)/ie; $val',
1629             },
1630             kywd => {
1631             Name => 'Keywords',
1632             # (4 byte flags, 2-byte lang, 1-byte count, count x pascal strings, ref 17)
1633             # (but I have also seen a simple string written by iPhone, so don't make writable yet)
1634             Notes => "not writable because Apple doesn't follow the 3gp specification",
1635             RawConv => q{
1636             my $sep = $self->Options('ListSep');
1637             return join($sep, split /\0+/, $val) unless $val =~ /^\0/; # (iPhone)
1638             return '' unless length $val >= 7;
1639             my $lang = Image::ExifTool::QuickTime::UnpackLang(Get16u(\$val, 4));
1640             $lang = $lang ? "($lang) " : '';
1641             my $num = Get8u(\$val, 6);
1642             my ($i, @vals);
1643             my $pos = 7;
1644             for ($i=0; $i<$num; ++$i) {
1645             last if $pos >= length $val;
1646             my $len = Get8u(\$val, $pos++);
1647             last if $pos + $len > length $val;
1648             my $v = substr($val, $pos, $len);
1649             $v = $self->Decode($v, 'UCS2') if $v =~ /^\xfe\xff/;
1650             push @vals, $v;
1651             $pos += $len;
1652             }
1653             return $lang . join($sep, @vals);
1654             },
1655             },
1656             loci => {
1657             Name => 'LocationInformation',
1658             Groups => { 2 => 'Location' },
1659             Writable => 'undef',
1660             IText => 6,
1661             Avoid => 1,
1662             NoDecode => 1, # (we'll decode the data ourself)
1663             Notes => q{
1664             string in the form "XXXXX Role=XXX Lat=XXX Lon=XXX Alt=XXX Body=XXX
1665             Notes=XXX", used in 3gp videos
1666             },
1667             # (4-byte flags, 2-byte lang, location string, 1-byte role, 4-byte fixed longitude,
1668             # 4-byte fixed latitude, 4-byte fixed altitude, body string, notes string)
1669             RawConv => q{
1670             my $str;
1671             if ($val =~ /^\xfe\xff/) {
1672             $val =~ s/^(\xfe\xff(.{2})*?)\0\0//s or return '';
1673             $str = $self->Decode($1, 'UCS2');
1674             } else {
1675             $val =~ s/^(.*?)\0//s or return '';
1676             $str = $self->Decode($1, 'UTF8');
1677             }
1678             $str = '(none)' unless length $str;
1679             return '' if length $val < 13;
1680             my $role = Get8u(\$val, 0);
1681             my $lon = GetFixed32s(\$val, 1);
1682             my $lat = GetFixed32s(\$val, 5);
1683             my $alt = GetFixed32s(\$val, 9);
1684             my $roleStr = {0=>'shooting',1=>'real',2=>'fictional',3=>'reserved'}->{$role};
1685             $str .= ' Role=' . ($roleStr || "unknown($role)");
1686             $str .= sprintf(' Lat=%.5f Lon=%.5f Alt=%.2f', $lat, $lon, $alt);
1687             $val = substr($val, 13);
1688             if ($val =~ s/^(\xfe\xff(.{2})*?)\0\0//s) {
1689             $str .= ' Body=' . $self->Decode($1, 'UCS2');
1690             } elsif ($val =~ s/^(.*?)\0//s) {
1691             $str .= ' Body=' . $self->Decode($1, 'UTF8');
1692             }
1693             if ($val =~ s/^(\xfe\xff(.{2})*?)\0\0//s) {
1694             $str .= ' Notes=' . $self->Decode($1, 'UCS2');
1695             } elsif ($val =~ s/^(.*?)\0//s) {
1696             $str .= ' Notes=' . $self->Decode($1, 'UTF8');
1697             }
1698             return $str;
1699             },
1700             RawConvInv => q{
1701             my ($role, $lat, $lon, $alt, $body, $note);
1702             $lat = $1 if $val =~ s/ Lat=([-+]?[.\d]+)//i;
1703             $lon = $1 if $val =~ s/ Lon=([-+]?[.\d]+)//i;
1704             $alt = $1 if $val =~ s/ Alt=([-+]?[.\d]+)//i;
1705             $note = $val =~ s/ Notes=(.*)//i ? $1 : '';
1706             $body = $val =~ s/ Body=(.*)//i ? $1 : '';
1707             $role = $val =~ s/ Role=(.*)//i ? $1 : '';
1708             $val = '' if $val eq '(none)';
1709             $role = {shooting=>0,real=>1,fictional=>2}->{lc $role} || 0;
1710             return $self->Encode($val, 'UTF8') . "\0" . Set8u($role) .
1711             SetFixed32s(defined $lon ? $lon : 999) .
1712             SetFixed32s(defined $lat ? $lat : 999) .
1713             SetFixed32s(defined $alt ? $alt : 0) .
1714             $self->Encode($body) . "\0" .
1715             $self->Encode($note) . "\0";
1716             },
1717             },
1718             yrrc => {
1719             Name => 'Year',
1720             Writable => 'undef',
1721             Groups => { 2 => 'Time' },
1722             Avoid => 1,
1723             Notes => 'used in 3gp videos',
1724             ValueConv => 'length($val) >= 6 ? unpack("x4n",$val) : ""',
1725             ValueConvInv => 'pack("Nn",0,$val)',
1726             },
1727             urat => { #17
1728             Name => 'UserRating',
1729             Writable => 'undef',
1730             Notes => 'used in 3gp videos',
1731             Avoid => 1,
1732             ValueConv => q{
1733             return '' unless length $val >= 8;
1734             unpack('x7C', $val);
1735             },
1736             ValueConvInv => 'pack("N2",0,$val)',
1737             },
1738             # tsel - TrackSelection (ref 17)
1739             # Apple tags (ref 16[dead] -- see ref 25 instead)
1740             angl => { Name => 'CameraAngle', Format => 'string' }, # (NC)
1741             clfn => { Name => 'ClipFileName', Format => 'string' }, # (NC)
1742             clid => { Name => 'ClipID', Format => 'string' }, # (NC)
1743             cmid => { Name => 'CameraID', Format => 'string' }, # (NC)
1744             cmnm => { # (NC)
1745             Name => 'Model',
1746             Description => 'Camera Model Name',
1747             Avoid => 1,
1748             Format => 'string', # (necessary to remove the trailing NULL)
1749             },
1750             date => {
1751             Name => 'DateTimeOriginal',
1752             Description => 'Date/Time Original',
1753             Groups => { 2 => 'Time' },
1754             Notes => q{
1755             Apple Photos has been reported to show a crazy date/time for some MP4 files
1756             containing this tag, but perhaps only if it is missing a time zone
1757             }, #forum10690/11125
1758             Shift => 'Time',
1759             ValueConv => q{
1760             require Image::ExifTool::XMP;
1761             $val = Image::ExifTool::XMP::ConvertXMPDate($val);
1762             $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
1763             return $val;
1764             },
1765             ValueConvInv => q{
1766             require Image::ExifTool::XMP;
1767             $val = Image::ExifTool::XMP::FormatXMPDate($val);
1768             $val =~ s/([-+]\d{2}):(\d{2})$/$1$2/; # remove time zone colon
1769             return $val;
1770             },
1771             PrintConv => '$self->ConvertDateTime($val)',
1772             PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
1773             },
1774             manu => { # (SX280)
1775             Name => 'Make',
1776             Avoid => 1,
1777             # (with Canon there are 6 unknown bytes before the model: "\0\0\0\0\x15\xc7")
1778             RawConv => '$val=~s/^\0{4}..//s; $val=~s/\0.*//; $val',
1779             },
1780             modl => { # (Samsung GT-S8530, Canon SX280)
1781             Name => 'Model',
1782             Description => 'Camera Model Name',
1783             Avoid => 1,
1784             # (with Canon there are 6 unknown bytes before the model: "\0\0\0\0\x15\xc7")
1785             RawConv => '$val=~s/^\0{4}..//s; $val=~s/\0.*//; $val',
1786             },
1787             reel => { Name => 'ReelName', Format => 'string' }, # (NC)
1788             scen => { Name => 'Scene', Format => 'string' }, # (NC)
1789             shot => { Name => 'ShotName', Format => 'string' }, # (NC)
1790             slno => { Name => 'SerialNumber', Format => 'string' }, # (NC)
1791             apmd => { Name => 'ApertureMode', Format => 'undef' }, #20
1792             kgtt => { #http://lists.ffmpeg.org/pipermail/ffmpeg-devel-irc/2012-June/000707.html
1793             # 'TrackType' will expand to 'Track#Type' when found inside a track
1794             Name => 'TrackType',
1795             # set flag to process this as international text
1796             # even though the tag ID doesn't start with 0xa9
1797             IText => 4, # IText with 4-byte header
1798             },
1799             chpl => { # (Nero chapter list)
1800             Name => 'ChapterList',
1801             ValueConv => \&ConvertChapterList,
1802             PrintConv => \&PrintChapter,
1803             },
1804             # ndrm - 7 bytes (0 0 0 1 0 0 0) Nero Digital Rights Management? (PH)
1805             # other non-Apple tags (ref 16)
1806             # hpix - HipixRichPicture (ref 16, HIPIX)
1807             # strk - sub-track information (ref 16, ISO)
1808             #
1809             # Manufacturer-specific metadata
1810             #
1811             TAGS => [ #PH
1812             # these tags were initially discovered in a Pentax movie,
1813             # but similar information is found in videos from other manufacturers
1814             {
1815             Name => 'FujiFilmTags',
1816             Condition => '$$valPt =~ /^FUJIFILM DIGITAL CAMERA\0/',
1817             SubDirectory => {
1818             TagTable => 'Image::ExifTool::FujiFilm::MOV',
1819             ByteOrder => 'LittleEndian',
1820             },
1821             },
1822             {
1823             Name => 'KodakTags',
1824             Condition => '$$valPt =~ /^EASTMAN KODAK COMPANY/',
1825             SubDirectory => {
1826             TagTable => 'Image::ExifTool::Kodak::MOV',
1827             ByteOrder => 'LittleEndian',
1828             },
1829             },
1830             {
1831             Name => 'KonicaMinoltaTags',
1832             Condition => '$$valPt =~ /^KONICA MINOLTA DIGITAL CAMERA/',
1833             SubDirectory => {
1834             TagTable => 'Image::ExifTool::Minolta::MOV1',
1835             ByteOrder => 'LittleEndian',
1836             },
1837             },
1838             {
1839             Name => 'MinoltaTags',
1840             Condition => '$$valPt =~ /^MINOLTA DIGITAL CAMERA/',
1841             SubDirectory => {
1842             TagTable => 'Image::ExifTool::Minolta::MOV2',
1843             ByteOrder => 'LittleEndian',
1844             },
1845             },
1846             {
1847             Name => 'NikonTags',
1848             Condition => '$$valPt =~ /^NIKON DIGITAL CAMERA\0/',
1849             SubDirectory => {
1850             TagTable => 'Image::ExifTool::Nikon::MOV',
1851             ByteOrder => 'LittleEndian',
1852             },
1853             },
1854             {
1855             Name => 'OlympusTags1',
1856             Condition => '$$valPt =~ /^OLYMPUS DIGITAL CAMERA\0.{9}\x01\0/s',
1857             SubDirectory => {
1858             TagTable => 'Image::ExifTool::Olympus::MOV1',
1859             ByteOrder => 'LittleEndian',
1860             },
1861             },
1862             {
1863             Name => 'OlympusTags2',
1864             Condition => '$$valPt =~ /^OLYMPUS DIGITAL CAMERA(?!\0.{21}\x0a\0{3})/s',
1865             SubDirectory => {
1866             TagTable => 'Image::ExifTool::Olympus::MOV2',
1867             ByteOrder => 'LittleEndian',
1868             },
1869             },
1870             {
1871             Name => 'OlympusTags3',
1872             Condition => '$$valPt =~ /^OLYMPUS DIGITAL CAMERA\0/',
1873             SubDirectory => {
1874             TagTable => 'Image::ExifTool::Olympus::MP4',
1875             ByteOrder => 'LittleEndian',
1876             },
1877             },
1878             {
1879             Name => 'OlympusTags4',
1880             Condition => '$$valPt =~ /^.{16}OLYM\0/s',
1881             SubDirectory => {
1882             TagTable => 'Image::ExifTool::Olympus::MOV3',
1883             Start => 12,
1884             },
1885             },
1886             {
1887             Name => 'PentaxTags',
1888             Condition => '$$valPt =~ /^PENTAX DIGITAL CAMERA\0/',
1889             SubDirectory => {
1890             TagTable => 'Image::ExifTool::Pentax::MOV',
1891             ByteOrder => 'LittleEndian',
1892             },
1893             },
1894             {
1895             Name => 'SamsungTags',
1896             Condition => '$$valPt =~ /^SAMSUNG DIGITAL CAMERA\0/',
1897             SubDirectory => {
1898             TagTable => 'Image::ExifTool::Samsung::MP4',
1899             ByteOrder => 'LittleEndian',
1900             },
1901             },
1902             {
1903             Name => 'SanyoMOV',
1904             Condition => q{
1905             $$valPt =~ /^SANYO DIGITAL CAMERA\0/ and
1906             $self->{VALUE}->{FileType} eq "MOV"
1907             },
1908             SubDirectory => {
1909             TagTable => 'Image::ExifTool::Sanyo::MOV',
1910             ByteOrder => 'LittleEndian',
1911             },
1912             },
1913             {
1914             Name => 'SanyoMP4',
1915             Condition => q{
1916             $$valPt =~ /^SANYO DIGITAL CAMERA\0/ and
1917             $self->{VALUE}->{FileType} eq "MP4"
1918             },
1919             SubDirectory => {
1920             TagTable => 'Image::ExifTool::Sanyo::MP4',
1921             ByteOrder => 'LittleEndian',
1922             },
1923             },
1924             {
1925             Name => 'UnknownTags',
1926             Unknown => 1,
1927             Binary => 1
1928             },
1929             ],
1930             # ---- Canon ----
1931             CNCV => { Name => 'CompressorVersion', Format => 'string' }, #PH (5D Mark II)
1932             CNMN => {
1933             Name => 'Model', #PH (EOS 550D)
1934             Description => 'Camera Model Name',
1935             Avoid => 1,
1936             Format => 'string', # (necessary to remove the trailing NULL)
1937             },
1938             CNFV => { Name => 'FirmwareVersion', Format => 'string' }, #PH (EOS 550D)
1939             CNTH => { #PH (PowerShot S95)
1940             Name => 'CanonCNTH',
1941             SubDirectory => { TagTable => 'Image::ExifTool::Canon::CNTH' },
1942             },
1943             CNOP => { #PH (7DmkII)
1944             Name => 'CanonCNOP',
1945             SubDirectory => { TagTable => 'Image::ExifTool::Canon::CNOP' },
1946             },
1947             # CNDB - 2112 bytes (550D)
1948             # CNDM - 4 bytes - 0xff,0xd8,0xff,0xd9 (S95)
1949             # CNDG - 10232 bytes, mostly zeros (N100)
1950             # ---- Casio ----
1951             QVMI => { #PH
1952             Name => 'CasioQVMI',
1953             # Casio stores standard EXIF-format information in MOV videos (eg. EX-S880)
1954             SubDirectory => {
1955             TagTable => 'Image::ExifTool::Exif::Main',
1956             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
1957             DirName => 'IFD0',
1958             Multi => 0, # (no NextIFD pointer)
1959             Start => 10,
1960             ByteOrder => 'BigEndian',
1961             },
1962             },
1963             # ---- FujiFilm ----
1964             FFMV => { #PH (FinePix HS20EXR)
1965             Name => 'FujiFilmFFMV',
1966             SubDirectory => { TagTable => 'Image::ExifTool::FujiFilm::FFMV' },
1967             },
1968             MVTG => { #PH (FinePix HS20EXR)
1969             Name => 'FujiFilmMVTG',
1970             SubDirectory => {
1971             TagTable => 'Image::ExifTool::Exif::Main',
1972             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
1973             DirName => 'IFD0',
1974             Start => 16,
1975             Base => '$start',
1976             ByteOrder => 'LittleEndian',
1977             },
1978             },
1979             # ---- Garmin ---- (ref PH)
1980             uuid => [{
1981             Name => 'GarminSoftware', # (NC)
1982             Condition => '$$valPt =~ /^VIRBactioncamera/',
1983             RawConv => 'substr($val, 16)',
1984             RawConvInv => '"VIRBactioncamera$val"',
1985             },{
1986             # 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)
1987             Name => 'UUID-Unknown',
1988             Writable => 0,
1989             %unknownInfo,
1990             }],
1991             pmcc => {
1992             Name => 'GarminSettings',
1993             ValueConv => 'substr($val, 4)',
1994             ValueConvInv => '"\0\0\0\x01$val"',
1995             },
1996             # hmtp - "\0\0\0\x01" followed by 408 bytes of zero
1997             # vrin - "\0\0\0\x01" followed by 8 bytes of zero
1998             # ---- GoPro ---- (ref PH)
1999             GoPr => 'GoProType', # (Hero3+)
2000             FIRM => { Name => 'FirmwareVersion', Avoid => 1 }, # (Hero4)
2001             LENS => 'LensSerialNumber', # (Hero4)
2002             CAME => { # (Hero4)
2003             Name => 'SerialNumberHash',
2004             Description => 'Camera Serial Number Hash',
2005             ValueConv => 'unpack("H*",$val)',
2006             ValueConvInv => 'pack("H*",$val)',
2007             },
2008             # SETT? 12 bytes (Hero4)
2009             # MUID? 32 bytes (Hero4, starts with serial number hash)
2010             # HMMT? 404 bytes (Hero4, all zero)
2011             # BCID? 26 bytes (Hero5, all zero), 36 bytes GoPro Max
2012             # GUMI? 16 bytes (Hero5)
2013             "FOV\0" => 'FieldOfView', #forum8938 (Hero2) seen: "Wide"
2014             GPMF => {
2015             Name => 'GoProGPMF',
2016             SubDirectory => { TagTable => 'Image::ExifTool::GoPro::GPMF' },
2017             },
2018             # free (all zero)
2019             "\xa9TSC" => 'StartTimeScale', # (Hero6)
2020             "\xa9TSZ" => 'StartTimeSampleSize', # (Hero6)
2021             "\xa9TIM" => 'StartTimecode', #PH (NC)
2022             # --- HTC ----
2023             htcb => {
2024             Name => 'HTCBinary',
2025             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HTCBinary' },
2026             },
2027             # ---- Kodak ----
2028             DcMD => {
2029             Name => 'KodakDcMD',
2030             SubDirectory => { TagTable => 'Image::ExifTool::Kodak::DcMD' },
2031             },
2032             SNum => { Name => 'SerialNumber', Avoid => 1, Groups => { 2 => 'Camera' } },
2033             ptch => { Name => 'Pitch', Format => 'rational64s', Avoid => 1 }, # Units??
2034             _yaw => { Name => 'Yaw', Format => 'rational64s', Avoid => 1 }, # Units??
2035             roll => { Name => 'Roll', Format => 'rational64s', Avoid => 1 }, # Units??
2036             _cx_ => { Name => 'CX', Format => 'rational64s', Unknown => 1 },
2037             _cy_ => { Name => 'CY', Format => 'rational64s', Unknown => 1 },
2038             rads => { Name => 'Rads', Format => 'rational64s', Unknown => 1 },
2039             lvlm => { Name => 'LevelMeter', Format => 'rational64s', Unknown => 1 }, # (guess)
2040             Lvlm => { Name => 'LevelMeter', Format => 'rational64s', Unknown => 1 }, # (guess)
2041             pose => { Name => 'pose', SubDirectory => { TagTable => 'Image::ExifTool::Kodak::pose' } },
2042             # AMBA => Ambarella AVC atom (unknown data written by Kodak Playsport video cam)
2043             # tmlp - 1 byte: 0 (PixPro SP360/4KVR360)
2044             # pivi - 72 bytes (PixPro SP360)
2045             # pive - 12 bytes (PixPro SP360)
2046             # loop - 4 bytes: 0 0 0 0 (PixPro 4KVR360)
2047             # m cm - 2 bytes: 0 0 (PixPro 4KVR360)
2048             # m ev - 2 bytes: 0 0 (PixPro SP360/4KVR360) (exposure comp?)
2049             # m vr - 2 bytes: 0 1 (PixPro 4KVR360) (virtual reality?)
2050             # m wb - 4 bytes: 0 0 0 0 (PixPro SP360/4KVR360) (white balance?)
2051             # mclr - 4 bytes: 0 0 0 0 (PixPro SP360/4KVR360)
2052             # mmtr - 4 bytes: 0,6 0 0 0 (PixPro SP360/4KVR360)
2053             # mflr - 4 bytes: 0 0 0 0 (PixPro SP360)
2054             # lvlm - 24 bytes (PixPro SP360)
2055             # Lvlm - 24 bytes (PixPro 4KVR360)
2056             # ufdm - 4 bytes: 0 0 0 1 (PixPro SP360)
2057             # mtdt - 1 byte: 0 (PixPro SP360/4KVR360)
2058             # gdta - 75240 bytes (PixPro SP360)
2059             # EIS1 - 4 bytes: 03 07 00 00 (PixPro 4KVR360)
2060             # EIS2 - 4 bytes: 04 97 00 00 (PixPro 4KVR360)
2061             # ---- LG ----
2062             adzc => { Name => 'Unknown_adzc', Unknown => 1, Hidden => 1, %langText }, # "false\0/","true\0/"
2063             adze => { Name => 'Unknown_adze', Unknown => 1, Hidden => 1, %langText }, # "false\0/"
2064             adzm => { Name => 'Unknown_adzm', Unknown => 1, Hidden => 1, %langText }, # "\x0e\x04/","\x10\x06"
2065             # ---- Microsoft ----
2066             Xtra => { #PH (microsoft)
2067             Name => 'MicrosoftXtra',
2068             WriteGroup => 'Microsoft',
2069             SubDirectory => {
2070             DirName => 'Microsoft',
2071             TagTable => 'Image::ExifTool::Microsoft::Xtra',
2072             },
2073             },
2074             # ---- Minolta ----
2075             MMA0 => { #PH (DiMage 7Hi)
2076             Name => 'MinoltaMMA0',
2077             SubDirectory => { TagTable => 'Image::ExifTool::Minolta::MMA' },
2078             },
2079             MMA1 => { #PH (Dimage A2)
2080             Name => 'MinoltaMMA1',
2081             SubDirectory => { TagTable => 'Image::ExifTool::Minolta::MMA' },
2082             },
2083             # ---- Nikon ----
2084             NCDT => { #PH
2085             Name => 'NikonNCDT',
2086             SubDirectory => { TagTable => 'Image::ExifTool::Nikon::NCDT' },
2087             },
2088             # ---- Olympus ----
2089             scrn => { #PH (TG-810)
2090             Name => 'OlympusPreview',
2091             Condition => '$$valPt =~ /^.{4}\xff\xd8\xff\xdb/s',
2092             SubDirectory => { TagTable => 'Image::ExifTool::Olympus::scrn' },
2093             },
2094             # ---- Panasonic/Leica ----
2095             PANA => { #PH
2096             Name => 'PanasonicPANA',
2097             SubDirectory => { TagTable => 'Image::ExifTool::Panasonic::PANA' },
2098             },
2099             LEIC => { #PH
2100             Name => 'LeicaLEIC',
2101             SubDirectory => { TagTable => 'Image::ExifTool::Panasonic::PANA' },
2102             },
2103             # ---- Pentax ----
2104             thmb => [ # (apparently defined by 3gpp, ref 16)
2105             { #PH (Pentax Q)
2106             Name => 'MakerNotePentax5a',
2107             Condition => '$$valPt =~ /^PENTAX \0II/',
2108             SubDirectory => {
2109             TagTable => 'Image::ExifTool::Pentax::Main',
2110             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2111             Start => 10,
2112             Base => '$start - 10',
2113             ByteOrder => 'LittleEndian',
2114             },
2115             },{ #PH (TG-810)
2116             Name => 'OlympusThumbnail',
2117             Condition => '$$valPt =~ /^.{4}\xff\xd8\xff\xdb/s',
2118             SubDirectory => { TagTable => 'Image::ExifTool::Olympus::thmb' },
2119             },{ #17 (format is in bytes 3-7)
2120             Name => 'ThumbnailImage',
2121             Condition => '$$valPt =~ /^.{8}\xff\xd8\xff[\xdb\xe0]/s',
2122             Groups => { 2 => 'Preview' },
2123             RawConv => 'substr($val, 8)',
2124             Binary => 1,
2125             },{ #17 (format is in bytes 3-7)
2126             Name => 'ThumbnailPNG',
2127             Condition => '$$valPt =~ /^.{8}\x89PNG\r\n\x1a\n/s',
2128             Groups => { 2 => 'Preview' },
2129             RawConv => 'substr($val, 8)',
2130             Binary => 1,
2131             },{
2132             Name => 'UnknownThumbnail',
2133             Groups => { 2 => 'Preview' },
2134             Binary => 1,
2135             },
2136             ],
2137             PENT => { #PH
2138             Name => 'PentaxPENT',
2139             SubDirectory => {
2140             TagTable => 'Image::ExifTool::Pentax::PENT',
2141             ByteOrder => 'LittleEndian',
2142             },
2143             },
2144             PXTH => { #PH (Pentax K-01)
2145             Name => 'PentaxPreview',
2146             SubDirectory => { TagTable => 'Image::ExifTool::Pentax::PXTH' },
2147             },
2148             PXMN => [{ #PH (Pentax K-01)
2149             Name => 'MakerNotePentax5b',
2150             Condition => '$$valPt =~ /^PENTAX \0MM/',
2151             SubDirectory => {
2152             TagTable => 'Image::ExifTool::Pentax::Main',
2153             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2154             Start => 10,
2155             Base => '$start - 10',
2156             ByteOrder => 'BigEndian',
2157             },
2158             },{ #PH (Pentax 645Z)
2159             Name => 'MakerNotePentax5c',
2160             Condition => '$$valPt =~ /^PENTAX \0II/',
2161             SubDirectory => {
2162             TagTable => 'Image::ExifTool::Pentax::Main',
2163             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2164             Start => 10,
2165             Base => '$start - 10',
2166             ByteOrder => 'LittleEndian',
2167             },
2168             },{
2169             Name => 'MakerNotePentaxUnknown',
2170             Binary => 1,
2171             }],
2172             # ---- Ricoh ----
2173             RTHU => { #PH (GR)
2174             Name => 'PreviewImage',
2175             Groups => { 2 => 'Preview' },
2176             RawConv => '$self->ValidateImage(\$val, $tag)',
2177             },
2178             RMKN => { #PH (GR)
2179             Name => 'RicohRMKN',
2180             SubDirectory => {
2181             TagTable => 'Image::ExifTool::Exif::Main',
2182             ProcessProc => \&Image::ExifTool::ProcessTIFF, # (because ProcessMOV is default)
2183             },
2184             },
2185             '@mak' => { Name => 'Make', Avoid => 1 },
2186             '@mod' => { Name => 'Model', Avoid => 1 },
2187             '@swr' => { Name => 'SoftwareVersion', Avoid => 1 },
2188             '@day' => {
2189             Name => 'ContentCreateDate',
2190             Notes => q{
2191             some stupid Ricoh programmer used the '@' symbol instead of the copyright
2192             symbol in these tag ID's for the Ricoh Theta Z1 and maybe other models
2193             },
2194             Groups => { 2 => 'Time' },
2195             Shift => 'Time',
2196             Avoid => 1,
2197             # handle values in the form "2010-02-12T13:27:14-0800"
2198             ValueConv => q{
2199             require Image::ExifTool::XMP;
2200             $val = Image::ExifTool::XMP::ConvertXMPDate($val);
2201             $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
2202             return $val;
2203             },
2204             ValueConvInv => q{
2205             require Image::ExifTool::XMP;
2206             my $tmp = Image::ExifTool::XMP::FormatXMPDate($val);
2207             ($val = $tmp) =~ s/([-+]\d{2}):(\d{2})$/$1$2/ if defined $tmp; # remove time zone colon
2208             return $val;
2209             },
2210             PrintConv => '$self->ConvertDateTime($val)',
2211             PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
2212             },
2213             '@xyz' => { #PH (iPhone 3GS)
2214             Name => 'GPSCoordinates',
2215             Groups => { 2 => 'Location' },
2216             Avoid => 1,
2217             ValueConv => \&ConvertISO6709,
2218             ValueConvInv => \&ConvInvISO6709,
2219             PrintConv => \&PrintGPSCoordinates,
2220             PrintConvInv => \&PrintInvGPSCoordinates,
2221             },
2222             # RDT1 - pairs of int32u_BE, starting at byte 8: "458275 471846"
2223             # RDT2 - pairs of int32u_BE, starting at byte 8: "472276 468526"
2224             # RDT3 - pairs of int32u_BE, starting at byte 8: "876603 482191"
2225             # RDT4 - pairs of int32u_BE, starting at byte 8: "1955 484612"
2226             # RDT6 - empty
2227             # RDT7 - empty
2228             # RDT8 - empty
2229             # RDT9 - only 16-byte header?
2230             # the boxes below all have a similar header (little-endian):
2231             # 0 int32u - number of records
2232             # 4 ? - "1e 00"
2233             # 6 int16u - record length in bytes
2234             # 8 ? - "23 01 00 00 00 00 00 00"
2235             # 16 - start of records (each record ends in an int64u timestamp "ts" in ns)
2236             # RDTA - float[4],ts: "-0.31289672 -0.2245330 11.303817 0 775.780"
2237             # RDTB - float[4],ts: "-0.04841613 -0.2166595 0.0724792 0 775.780"
2238             # RDTC - float[4],ts: "27.60925 -27.10037 -13.27285 0 775.829"
2239             # RDTD - int16s[3],ts: "353 -914 16354 0 775.829"
2240             # RDTG - ts: "775.825"
2241             # RDTI - float[4],ts: "0.00165951 0.005770059 0.06838259 0.1744695 775.862"
2242             # ---- Samsung ----
2243             vndr => 'Vendor', #PH (Samsung PL70)
2244             SDLN => 'PlayMode', #PH (NC, Samsung ST80 "SEQ_PLAY")
2245             INFO => {
2246             Name => 'SamsungINFO',
2247             SubDirectory => { TagTable => 'Image::ExifTool::Samsung::INFO' },
2248             },
2249             '@sec' => { #PH (Samsung WB30F)
2250             Name => 'SamsungSec',
2251             SubDirectory => { TagTable => 'Image::ExifTool::Samsung::sec' },
2252             },
2253             'smta' => { #PH (Samsung SM-C101)
2254             Name => 'SamsungSmta',
2255             SubDirectory => {
2256             TagTable => 'Image::ExifTool::Samsung::smta',
2257             Start => 4,
2258             },
2259             },
2260             cver => 'CodeVersion', #PH (guess, Samsung MV900F)
2261             # ducp - 4 bytes all zero (Samsung ST96,WB750), 52 bytes all zero (Samsung WB30F)
2262             # edli - 52 bytes all zero (Samsung WB30F)
2263             # @etc - 4 bytes all zero (Samsung WB30F)
2264             # saut - 4 bytes all zero (Samsung SM-N900T)
2265             # smrd - string "TRUEBLUE" (Samsung SM-C101)
2266             # ---- TomTom Bandit Action Cam ----
2267             TTMD => {
2268             Name => 'TomTomMetaData',
2269             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TomTom' },
2270             },
2271             # ---- Samsung Gear 360 ----
2272             vrot => {
2273             Name => 'AccelerometerData',
2274             Notes => q{
2275             accelerometer readings for each frame of the video, expressed as sets of
2276             yaw, pitch and roll angles in degrees
2277             },
2278             Format => 'rational64s',
2279             ValueConv => '$val =~ s/^-?\d+ //; \$val', # (ignore leading version/size words)
2280             },
2281             # m360 - 8 bytes "0 0 0 0 0 0 0 1"
2282             # opax - 164 bytes unknown (center and affine arrays? ref 26)
2283             # opai - 32 bytes (maybe contains a serial number starting at byte 16? - PH) (rgb gains, degamma, gamma? ref 26)
2284             # intv - 16 bytes all zero
2285             # ---- Xaiomi ----
2286             mcvr => {
2287             Name => 'PreviewImage',
2288             Groups => { 2 => 'Preview' },
2289             Binary => 1,
2290             },
2291             # ---- Unknown ----
2292             # CDET - 128 bytes (unknown origin)
2293             # mtyp - 4 bytes all zero (some drone video)
2294             # kgrf - 8 bytes all zero ? (in udta inside trak atom)
2295             # kgcg - 128 bytes 0's and 1's
2296             # kgsi - 4 bytes "00 00 00 80"
2297             # FIEL - 18 bytes "FIEL\0\x01\0\0\0..."
2298             #
2299             # other 3rd-party tags
2300             # (ref http://code.google.com/p/mp4parser/source/browse/trunk/isoparser/src/main/resources/isoparser-default.properties?r=814)
2301             #
2302             ccid => 'ContentID',
2303             icnu => 'IconURI',
2304             infu => 'InfoURL',
2305             cdis => 'ContentDistributorID',
2306             albr => { Name => 'AlbumArtist', Groups => { 2 => 'Author' } },
2307             cvru => 'CoverURI',
2308             lrcu => 'LyricsURI',
2309              
2310             tags => { # found in Audible .m4b audio books (ref PH)
2311             Name => 'Audible_tags',
2312             SubDirectory => { TagTable => 'Image::ExifTool::Audible::tags' },
2313             },
2314             );
2315              
2316             # Unknown information stored in HTC One (M8) videos - PH
2317             %Image::ExifTool::QuickTime::HTCBinary = (
2318             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2319             GROUPS => { 0 => 'MakerNotes', 1 => 'HTC', 2 => 'Video' },
2320             TAG_PREFIX => 'HTCBinary',
2321             FORMAT => 'int32u',
2322             FIRST_ENTRY => 0,
2323             # 0 - values: 1
2324             # 1 - values: 0
2325             # 2 - values: 0
2326             # 3 - values: FileSize minus 12 (why?)
2327             # 4 - values: 12
2328             );
2329              
2330             # TomTom Bandit Action Cam metadata (ref PH)
2331             %Image::ExifTool::QuickTime::TomTom = (
2332             PROCESS_PROC => \&ProcessMOV,
2333             GROUPS => { 2 => 'Video' },
2334             NOTES => 'Tags found in TomTom Bandit Action Cam MP4 videos.',
2335             TTAD => {
2336             Name => 'TomTomAD',
2337             SubDirectory => {
2338             TagTable => 'Image::ExifTool::QuickTime::Stream',
2339             ProcessProc => \&Image::ExifTool::QuickTime::ProcessTTAD,
2340             },
2341             },
2342             TTHL => { Name => 'TomTomHL', Binary => 1, Unknown => 1 }, # (mostly zeros)
2343             # (TTID values are different for each video)
2344             TTID => { Name => 'TomTomID', ValueConv => 'unpack("x4H*",$val)' },
2345             TTVI => { Name => 'TomTomVI', Format => 'int32u', Unknown => 1 }, # seen: "0 1 61 508 508"
2346             # TTVD seen: "normal 720p 60fps 60fps 16/9 wide 1x"
2347             TTVD => { Name => 'TomTomVD', ValueConv => 'my @a = ($val =~ /[\x20-\x7f]+/g); "@a"' },
2348             );
2349              
2350             # User-specific media data atoms (ref 11)
2351             %Image::ExifTool::QuickTime::UserMedia = (
2352             PROCESS_PROC => \&ProcessMOV,
2353             GROUPS => { 2 => 'Video' },
2354             MTDT => {
2355             Name => 'MetaData',
2356             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MetaData' },
2357             },
2358             );
2359              
2360             # User-specific media data atoms (ref 11)
2361             %Image::ExifTool::QuickTime::MetaData = (
2362             PROCESS_PROC => \&ProcessMetaData,
2363             GROUPS => { 2 => 'Video' },
2364             TAG_PREFIX => 'MetaData',
2365             0x01 => 'Title',
2366             0x03 => {
2367             Name => 'ProductionDate',
2368             Groups => { 2 => 'Time' },
2369             Shift => 'Time',
2370             Writable => 1,
2371             Permanent => 1,
2372             DelValue => '0000/00/00 00:00:00',
2373             # translate from format "YYYY/mm/dd HH:MM:SS"
2374             ValueConv => '$val=~tr{/}{:}; $val',
2375             ValueConvInv => '$val=~s[^(\d{4}):(\d{2}):][$1/$2/]; $val',
2376             PrintConv => '$self->ConvertDateTime($val)',
2377             PrintConvInv => '$self->InverseDateTime($val)',
2378             },
2379             0x04 => 'Software',
2380             0x05 => 'Product',
2381             0x0a => {
2382             Name => 'TrackProperty',
2383             RawConv => 'my @a=unpack("Nnn",$val); "@a"',
2384             PrintConv => [
2385             { 0 => 'No presentation', BITMASK => { 0 => 'Main track' } },
2386             { 0 => 'No attributes', BITMASK => { 15 => 'Read only' } },
2387             '"Priority $val"',
2388             ],
2389             },
2390             0x0b => {
2391             Name => 'TimeZone',
2392             Groups => { 2 => 'Time' },
2393             Writable => 1,
2394             Permanent => 1,
2395             DelValue => 0,
2396             RawConv => 'Get16s(\$val,0)',
2397             RawConvInv => 'Set16s($val)',
2398             PrintConv => 'TimeZoneString($val)',
2399             PrintConvInv => q{
2400             return undef unless $val =~ /^([-+])(\d{1,2}):?(\d{2})$/'
2401             my $tzmin = $2 * 60 + $3;
2402             $tzmin = -$tzmin if $1 eq '-';
2403             return $tzmin;
2404             }
2405             },
2406             0x0c => {
2407             Name => 'ModifyDate',
2408             Groups => { 2 => 'Time' },
2409             Shift => 'Time',
2410             Writable => 1,
2411             Permanent => 1,
2412             DelValue => '0000/00/00 00:00:00',
2413             # translate from format "YYYY/mm/dd HH:MM:SS"
2414             ValueConv => '$val=~tr{/}{:}; $val',
2415             ValueConvInv => '$val=~s[^(\d{4}):(\d{2}):][$1/$2/]; $val',
2416             PrintConv => '$self->ConvertDateTime($val)',
2417             PrintConvInv => '$self->InverseDateTime($val)',
2418             },
2419             );
2420              
2421             # compressed movie atoms (ref http://wiki.multimedia.cx/index.php?title=QuickTime_container#cmov)
2422             %Image::ExifTool::QuickTime::CMovie = (
2423             PROCESS_PROC => \&ProcessMOV,
2424             GROUPS => { 2 => 'Video' },
2425             dcom => 'Compression',
2426             # cmvd - compressed moov atom data
2427             );
2428              
2429             # Profile atoms (ref 11)
2430             %Image::ExifTool::QuickTime::Profile = (
2431             PROCESS_PROC => \&ProcessMOV,
2432             GROUPS => { 2 => 'Video' },
2433             FPRF => {
2434             Name => 'FileGlobalProfile',
2435             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::FileProf' },
2436             },
2437             APRF => {
2438             Name => 'AudioProfile',
2439             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AudioProf' },
2440             },
2441             VPRF => {
2442             Name => 'VideoProfile',
2443             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::VideoProf' },
2444             },
2445             OLYM => { #PH
2446             Name => 'OlympusOLYM',
2447             SubDirectory => {
2448             TagTable => 'Image::ExifTool::Olympus::OLYM',
2449             ByteOrder => 'BigEndian',
2450             },
2451             },
2452             );
2453              
2454             # FPRF atom information (ref 11)
2455             %Image::ExifTool::QuickTime::FileProf = (
2456             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2457             GROUPS => { 2 => 'Video' },
2458             FORMAT => 'int32u',
2459             0 => { Name => 'FileProfileVersion', Unknown => 1 }, # unknown = uninteresting
2460             1 => {
2461             Name => 'FileFunctionFlags',
2462             PrintConv => { BITMASK => {
2463             28 => 'Fragmented',
2464             29 => 'Additional tracks',
2465             30 => 'Edited', # (main AV track is edited)
2466             }},
2467             },
2468             # 2 - reserved
2469             );
2470              
2471             # APRF atom information (ref 11)
2472             %Image::ExifTool::QuickTime::AudioProf = (
2473             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2474             GROUPS => { 2 => 'Audio' },
2475             FORMAT => 'int32u',
2476             0 => { Name => 'AudioProfileVersion', Unknown => 1 },
2477             1 => 'AudioTrackID',
2478             2 => {
2479             Name => 'AudioCodec',
2480             Format => 'undef[4]',
2481             },
2482             3 => {
2483             Name => 'AudioCodecInfo',
2484             Unknown => 1,
2485             PrintConv => 'sprintf("0x%.4x", $val)',
2486             },
2487             4 => {
2488             Name => 'AudioAttributes',
2489             PrintConv => { BITMASK => {
2490             0 => 'Encrypted',
2491             1 => 'Variable bitrate',
2492             2 => 'Dual mono',
2493             }},
2494             },
2495             5 => {
2496             Name => 'AudioAvgBitrate',
2497             ValueConv => '$val * 1000',
2498             PrintConv => 'ConvertBitrate($val)',
2499             },
2500             6 => {
2501             Name => 'AudioMaxBitrate',
2502             ValueConv => '$val * 1000',
2503             PrintConv => 'ConvertBitrate($val)',
2504             },
2505             7 => 'AudioSampleRate',
2506             8 => 'AudioChannels',
2507             );
2508              
2509             # VPRF atom information (ref 11)
2510             %Image::ExifTool::QuickTime::VideoProf = (
2511             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2512             GROUPS => { 2 => 'Video' },
2513             FORMAT => 'int32u',
2514             0 => { Name => 'VideoProfileVersion', Unknown => 1 },
2515             1 => 'VideoTrackID',
2516             2 => {
2517             Name => 'VideoCodec',
2518             Format => 'undef[4]',
2519             },
2520             3 => {
2521             Name => 'VideoCodecInfo',
2522             Unknown => 1,
2523             PrintConv => 'sprintf("0x%.4x", $val)',
2524             },
2525             4 => {
2526             Name => 'VideoAttributes',
2527             PrintConv => { BITMASK => {
2528             0 => 'Encrypted',
2529             1 => 'Variable bitrate',
2530             2 => 'Variable frame rate',
2531             3 => 'Interlaced',
2532             }},
2533             },
2534             5 => {
2535             Name => 'VideoAvgBitrate',
2536             ValueConv => '$val * 1000',
2537             PrintConv => 'ConvertBitrate($val)',
2538             },
2539             6 => {
2540             Name => 'VideoMaxBitrate',
2541             ValueConv => '$val * 1000',
2542             PrintConv => 'ConvertBitrate($val)',
2543             },
2544             7 => {
2545             Name => 'VideoAvgFrameRate',
2546             Format => 'fixed32u',
2547             PrintConv => 'int($val * 1000 + 0.5) / 1000',
2548             },
2549             8 => {
2550             Name => 'VideoMaxFrameRate',
2551             Format => 'fixed32u',
2552             PrintConv => 'int($val * 1000 + 0.5) / 1000',
2553             },
2554             9 => {
2555             Name => 'VideoSize',
2556             Format => 'int16u[2]',
2557             PrintConv => '$val=~tr/ /x/; $val',
2558             },
2559             10 => {
2560             Name => 'PixelAspectRatio',
2561             Format => 'int16u[2]',
2562             PrintConv => '$val=~tr/ /:/; $val',
2563             },
2564             );
2565              
2566             # meta atoms
2567             %Image::ExifTool::QuickTime::Meta = (
2568             PROCESS_PROC => \&ProcessMOV,
2569             WRITE_PROC => \&WriteQuickTime,
2570             GROUPS => { 1 => 'Meta', 2 => 'Video' },
2571             ilst => {
2572             Name => 'ItemList',
2573             SubDirectory => {
2574             TagTable => 'Image::ExifTool::QuickTime::ItemList',
2575             HasData => 1, # process atoms as containers with 'data' elements
2576             },
2577             },
2578             # MP4 tags (ref 5)
2579             hdlr => {
2580             Name => 'Handler',
2581             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Handler' },
2582             },
2583             dinf => {
2584             Name => 'DataInfo', # (don't change this name -- used to recognize directory when writing)
2585             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::DataInfo' },
2586             },
2587             ipmc => {
2588             Name => 'IPMPControl',
2589             Flags => ['Binary','Unknown'],
2590             },
2591             iloc => {
2592             Name => 'ItemLocation',
2593             RawConv => \&ParseItemLocation,
2594             WriteHook => \&ParseItemLocation,
2595             Notes => 'parsed, but not extracted as a tag',
2596             },
2597             ipro => {
2598             Name => 'ItemProtection',
2599             Flags => ['Binary','Unknown'],
2600             },
2601             iinf => [{
2602             Name => 'ItemInformation',
2603             Condition => '$$valPt =~ /^\0/', # (check for version 0)
2604             SubDirectory => {
2605             TagTable => 'Image::ExifTool::QuickTime::ItemInfo',
2606             Start => 6, # (4-byte version/flags + 2-byte count)
2607             },
2608             },{
2609             Name => 'ItemInformation',
2610             SubDirectory => {
2611             TagTable => 'Image::ExifTool::QuickTime::ItemInfo',
2612             Start => 8, # (4-byte version/flags + 4-byte count)
2613             },
2614             }],
2615             'xml ' => {
2616             Name => 'XML',
2617             Flags => [ 'Binary', 'Protected' ],
2618             SubDirectory => {
2619             TagTable => 'Image::ExifTool::XMP::XML',
2620             IgnoreProp => { NonRealTimeMeta => 1 }, # ignore container for Sony 'nrtm'
2621             },
2622             },
2623             'keys' => {
2624             Name => 'Keys',
2625             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Keys' },
2626             },
2627             bxml => {
2628             Name => 'BinaryXML',
2629             Flags => ['Binary','Unknown'],
2630             },
2631             pitm => [{
2632             Name => 'PrimaryItemReference',
2633             Condition => '$$valPt =~ /^\0/', # (version 0?)
2634             RawConv => '$$self{PrimaryItem} = unpack("x4n",$val)',
2635             WriteHook => sub { my ($val,$et) = @_; $$et{PrimaryItem} = unpack("x4n",$val); },
2636             },{
2637             Name => 'PrimaryItemReference',
2638             RawConv => '$$self{PrimaryItem} = unpack("x4N",$val)',
2639             WriteHook => sub { my ($val,$et) = @_; $$et{PrimaryItem} = unpack("x4N",$val); },
2640             }],
2641             free => { #PH
2642             Name => 'Free',
2643             Flags => ['Binary','Unknown'],
2644             },
2645             iprp => {
2646             Name => 'ItemProperties',
2647             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ItemProp' },
2648             },
2649             iref => {
2650             Name => 'ItemReference',
2651             # the version is needed to parse some of the item references
2652             Condition => '$$self{ItemRefVersion} = ord($$valPt); 1',
2653             SubDirectory => {
2654             TagTable => 'Image::ExifTool::QuickTime::ItemRef',
2655             Start => 4,
2656             },
2657             },
2658             idat => {
2659             Name => 'MetaImageSize', #PH (NC)
2660             Format => 'int16u',
2661             # (don't know what the first two numbers are for)
2662             PrintConv => '$val =~ s/^(\d+) (\d+) (\d+) (\d+)/${3}x$4/; $val',
2663             },
2664             uuid => [
2665             { #PH (Canon R5/R6 HIF)
2666             Name => 'MetaVersion', # (NC)
2667             Condition => '$$valPt=~/^\x85\xc0\xb6\x87\x82\x0f\x11\xe0\x81\x11\xf4\xce\x46\x2b\x6a\x48/',
2668             RawConv => 'substr($val, 0x14)',
2669             },
2670             {
2671             Name => 'UUID-Unknown',
2672             %unknownInfo,
2673             },
2674             ],
2675             );
2676              
2677             # additional metadata container (ref ISO14496-12:2015)
2678             %Image::ExifTool::QuickTime::OtherMeta = (
2679             PROCESS_PROC => \&ProcessMOV,
2680             WRITE_PROC => \&WriteQuickTime,
2681             GROUPS => { 2 => 'Video' },
2682             mere => {
2683             Name => 'MetaRelation',
2684             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MetaRelation' },
2685             },
2686             meta => {
2687             Name => 'Meta',
2688             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
2689             },
2690             );
2691              
2692             # metabox relation (ref ISO14496-12:2015)
2693             %Image::ExifTool::QuickTime::MetaRelation = (
2694             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2695             GROUPS => { 2 => 'Video' },
2696             FORMAT => 'int32u',
2697             # 0 => 'MetaRelationVersion',
2698             # 1 => 'FirstMetaboxHandlerType',
2699             # 2 => 'FirstMetaboxHandlerType',
2700             # 3 => { Name => 'MetaboxRelation', Format => 'int8u' },
2701             );
2702              
2703             %Image::ExifTool::QuickTime::ItemProp = (
2704             PROCESS_PROC => \&ProcessMOV,
2705             WRITE_PROC => \&WriteQuickTime,
2706             GROUPS => { 2 => 'Image' },
2707             ipco => {
2708             Name => 'ItemPropertyContainer',
2709             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ItemPropCont' },
2710             },
2711             ipma => {
2712             Name => 'ItemPropertyAssociation',
2713             RawConv => \&ParseItemPropAssoc,
2714             WriteHook => \&ParseItemPropAssoc,
2715             Notes => 'parsed, but not extracted as a tag',
2716             },
2717             );
2718              
2719             %Image::ExifTool::QuickTime::ItemPropCont = (
2720             PROCESS_PROC => \&ProcessMOV,
2721             WRITE_PROC => \&WriteQuickTime,
2722             PERMANENT => 1, # (can't be deleted)
2723             GROUPS => { 2 => 'Image' },
2724             VARS => { START_INDEX => 1 }, # show verbose indices starting at 1
2725             colr => [{
2726             Name => 'ICC_Profile',
2727             Condition => '$$valPt =~ /^(prof|rICC)/',
2728             Permanent => 0, # (in QuickTime, this writes a zero-length box instead of deleting)
2729             SubDirectory => {
2730             TagTable => 'Image::ExifTool::ICC_Profile::Main',
2731             Start => 4,
2732             },
2733             },{
2734             Name => 'ColorRepresentation',
2735             ValueConv => 'join(" ", substr($val,0,4), unpack("x4n*",$val))',
2736             }],
2737             irot => {
2738             Name => 'Rotation',
2739             Format => 'int8u',
2740             Writable => 'int8u',
2741             Protected => 1,
2742             ValueConv => '$val * 90',
2743             ValueConvInv => 'int($val / 90 + 0.5)',
2744             },
2745             ispe => {
2746             Name => 'ImageSpatialExtent',
2747             Condition => '$$valPt =~ /^\0{4}/', # (version/flags == 0/0)
2748             RawConv => q{
2749             my @dim = unpack("x4N*", $val);
2750             return undef if @dim < 2;
2751             unless ($$self{DOC_NUM}) {
2752             $self->FoundTag(ImageWidth => $dim[0]);
2753             $self->FoundTag(ImageHeight => $dim[1]);
2754             }
2755             return join ' ', @dim;
2756             },
2757             PrintConv => '$val =~ tr/ /x/; $val',
2758             },
2759             pixi => {
2760             Name => 'ImagePixelDepth',
2761             Condition => '$$valPt =~ /^\0{4}./s', # (version/flags == 0/0 and count)
2762             RawConv => 'join " ", unpack("x5C*", $val)',
2763             },
2764             auxC => {
2765             Name => 'AuxiliaryImageType',
2766             Format => 'undef',
2767             RawConv => '$val = substr($val, 4); $val =~ s/\0.*//s; $val',
2768             },
2769             pasp => {
2770             Name => 'PixelAspectRatio',
2771             Format => 'int32u',
2772             Writable => 'int32u',
2773             Protected => 1,
2774             },
2775             rloc => {
2776             Name => 'RelativeLocation',
2777             Format => 'int32u',
2778             RawConv => '$val =~ s/^\S+\s+//; $val', # remove version/flags
2779             },
2780             clap => {
2781             Name => 'CleanAperture',
2782             Format => 'rational64s',
2783             Notes => '4 numbers: width, height, left and top',
2784             },
2785             hvcC => {
2786             Name => 'HEVCConfiguration',
2787             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HEVCConfig' },
2788             },
2789             av1C => {
2790             Name => 'AV1Configuration',
2791             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AV1Config' },
2792             },
2793             );
2794              
2795             # HEVC configuration (ref https://github.com/MPEGGroup/isobmff/blob/master/IsoLib/libisomediafile/src/HEVCConfigAtom.c)
2796             %Image::ExifTool::QuickTime::HEVCConfig = (
2797             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2798             GROUPS => { 2 => 'Video' },
2799             FIRST_ENTRY => 0,
2800             0 => 'HEVCConfigurationVersion',
2801             1 => {
2802             Name => 'GeneralProfileSpace',
2803             Mask => 0xc0,
2804             PrintConv => { 0 => 'Conforming' },
2805             },
2806             1.1 => {
2807             Name => 'GeneralTierFlag',
2808             Mask => 0x20,
2809             PrintConv => {
2810             0 => 'Main Tier',
2811             1 => 'High Tier',
2812             },
2813             },
2814             1.2 => {
2815             Name => 'GeneralProfileIDC',
2816             Mask => 0x1f,
2817             PrintConv => {
2818             0 => 'No Profile',
2819             1 => 'Main',
2820             2 => 'Main 10',
2821             3 => 'Main Still Picture',
2822             4 => 'Format Range Extensions',
2823             5 => 'High Throughput',
2824             6 => 'Multiview Main',
2825             7 => 'Scalable Main',
2826             8 => '3D Main',
2827             9 => 'Screen Content Coding Extensions',
2828             10 => 'Scalable Format Range Extensions',
2829             11 => 'High Throughput Screen Content Coding Extensions',
2830             },
2831             },
2832             2 => {
2833             Name => 'GenProfileCompatibilityFlags',
2834             Format => 'int32u',
2835             PrintConv => { BITMASK => {
2836             31 => 'No Profile', # (bit 0 in stream)
2837             30 => 'Main', # (bit 1 in stream)
2838             29 => 'Main 10', # (bit 2 in stream)
2839             28 => 'Main Still Picture', # (bit 3 in stream)
2840             27 => 'Format Range Extensions',# (...)
2841             26 => 'High Throughput',
2842             25 => 'Multiview Main',
2843             24 => 'Scalable Main',
2844             23 => '3D Main',
2845             22 => 'Screen Content Coding Extensions',
2846             21 => 'Scalable Format Range Extensions',
2847             20 => 'High Throughput Screen Content Coding Extensions',
2848             }},
2849             },
2850             6 => {
2851             Name => 'ConstraintIndicatorFlags',
2852             Format => 'int8u[6]',
2853             },
2854             12 => {
2855             Name => 'GeneralLevelIDC',
2856             PrintConv => 'sprintf("%d (level %.1f)", $val, $val/30)',
2857             },
2858             13 => {
2859             Name => 'MinSpatialSegmentationIDC',
2860             Format => 'int16u',
2861             Mask => 0x0fff,
2862             },
2863             15 => {
2864             Name => 'ParallelismType',
2865             Mask => 0x03,
2866             },
2867             16 => {
2868             Name => 'ChromaFormat',
2869             Mask => 0x03,
2870             PrintConv => {
2871             0 => 'Monochrome',
2872             1 => '4:2:0',
2873             2 => '4:2:2',
2874             3 => '4:4:4',
2875             },
2876             },
2877             17 => {
2878             Name => 'BitDepthLuma',
2879             Mask => 0x07,
2880             ValueConv => '$val + 8',
2881             },
2882             18 => {
2883             Name => 'BitDepthChroma',
2884             Mask => 0x07,
2885             ValueConv => '$val + 8',
2886             },
2887             19 => {
2888             Name => 'AverageFrameRate',
2889             Format => 'int16u',
2890             ValueConv => '$val / 256',
2891             },
2892             21 => {
2893             Name => 'ConstantFrameRate',
2894             Mask => 0xc0,
2895             PrintConv => {
2896             0 => 'Unknown',
2897             1 => 'Constant Frame Rate',
2898             2 => 'Each Temporal Layer is Constant Frame Rate',
2899             },
2900             },
2901             21.1 => {
2902             Name => 'NumTemporalLayers',
2903             Mask => 0x38,
2904             },
2905             21.2 => {
2906             Name => 'TemporalIDNested',
2907             Mask => 0x04,
2908             PrintConv => { 0 => 'No', 1 => 'Yes' },
2909             },
2910             #21.3 => {
2911             # Name => 'NALUnitLengthSize',
2912             # Mask => 0x03,
2913             # ValueConv => '$val + 1',
2914             # PrintConv => { 1 => '8-bit', 2 => '16-bit', 4 => '32-bit' },
2915             #},
2916             #22 => 'NumberOfNALUnitArrays',
2917             # (don't decode the NAL unit arrays)
2918             );
2919              
2920             # HEVC configuration (ref https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox)
2921             %Image::ExifTool::QuickTime::AV1Config = (
2922             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2923             GROUPS => { 2 => 'Video' },
2924             FIRST_ENTRY => 0,
2925             0 => {
2926             Name => 'AV1ConfigurationVersion',
2927             Mask => 0x7f,
2928             },
2929             1.0 => {
2930             Name => 'SeqProfile',
2931             Mask => 0xe0,
2932             Unknown => 1,
2933             },
2934             1.1 => {
2935             Name => 'SeqLevelIdx0',
2936             Mask => 0x1f,
2937             Unknown => 1,
2938             },
2939             2.0 => {
2940             Name => 'SeqTier0',
2941             Mask => 0x80,
2942             Unknown => 1,
2943             },
2944             2.1 => {
2945             Name => 'HighBitDepth',
2946             Mask => 0x40,
2947             Unknown => 1,
2948             },
2949             2.2 => {
2950             Name => 'TwelveBit',
2951             Mask => 0x20,
2952             Unknown => 1,
2953             },
2954             2.3 => {
2955             Name => 'ChromaFormat', # (Monochrome+SubSamplingX+SubSamplingY)
2956             Notes => 'bits: 0x04 = Monochrome, 0x02 = SubSamplingX, 0x01 = SubSamplingY',
2957             Mask => 0x1c,
2958             PrintConv => {
2959             0x00 => 'YUV 4:4:4',
2960             0x02 => 'YUV 4:2:2',
2961             0x03 => 'YUV 4:2:0',
2962             0x07 => 'Monochrome 4:0:0',
2963             },
2964             },
2965             2.4 => {
2966             Name => 'ChromaSamplePosition',
2967             Mask => 0x03,
2968             PrintConv => {
2969             0 => 'Unknown',
2970             1 => 'Vertical',
2971             2 => 'Colocated',
2972             3 => '(reserved)',
2973             },
2974             },
2975             3 => {
2976             Name => 'InitialDelaySamples',
2977             RawConv => '$val & 0x10 ? undef : ($val & 0x0f) + 1',
2978             Unknown => 1,
2979             },
2980             );
2981              
2982             %Image::ExifTool::QuickTime::ItemRef = (
2983             PROCESS_PROC => \&ProcessMOV,
2984             WRITE_PROC => \&WriteQuickTime,
2985             GROUPS => { 2 => 'Image' },
2986             # (Note: ExifTool's ItemRefVersion may be used to test the iref version number)
2987             NOTES => q{
2988             The Item reference entries listed in the table below contain information about
2989             the associations between items in the file. This information is used by
2990             ExifTool, but these entries are not extracted as tags.
2991             },
2992             dimg => { Name => 'DerivedImageRef', RawConv => 'undef' },
2993             thmb => { Name => 'ThumbnailRef', RawConv => 'undef' },
2994             auxl => { Name => 'AuxiliaryImageRef', RawConv => 'undef' },
2995             cdsc => {
2996             Name => 'ContentDescribes',
2997             RawConv => \&ParseContentDescribes,
2998             WriteHook => \&ParseContentDescribes,
2999             },
3000             );
3001              
3002             %Image::ExifTool::QuickTime::ItemInfo = (
3003             PROCESS_PROC => \&ProcessMOV,
3004             WRITE_PROC => \&WriteQuickTime,
3005             GROUPS => { 2 => 'Image' },
3006             # avc1 - AVC image
3007             # hvc1 - HEVC image
3008             # lhv1 - L-HEVC image
3009             # infe - ItemInformationEntry
3010             # infe types: avc1,hvc1,lhv1,Exif,xml1,iovl(overlay image),grid,mime,hvt1(tile image)
3011             infe => {
3012             Name => 'ItemInfoEntry',
3013             RawConv => \&ParseItemInfoEntry,
3014             WriteHook => \&ParseItemInfoEntry,
3015             Notes => 'parsed, but not extracted as a tag',
3016             },
3017             );
3018              
3019             # track reference atoms
3020             %Image::ExifTool::QuickTime::TrackRef = (
3021             PROCESS_PROC => \&ProcessMOV,
3022             GROUPS => { 1 => 'Track#', 2 => 'Video' },
3023             chap => { Name => 'ChapterListTrackID', Format => 'int32u' },
3024             tmcd => { Name => 'TimeCode', Format => 'int32u' },
3025             mpod => { #PH (FLIR MP4)
3026             Name => 'ElementaryStreamTrack',
3027             Format => 'int32u',
3028             ValueConv => '$val =~ s/^1 //; $val', # (why 2 numbers? -- ignore the first if "1")
3029             },
3030             # also: sync, scpt, ssrc, iTunesInfo
3031             cdsc => {
3032             Name => 'ContentDescribes',
3033             Format => 'int32u',
3034             PrintConv => '"Track $val"',
3035             },
3036             # cdep (Structural Dependency QT tag?)
3037             );
3038              
3039             # track aperture mode dimensions atoms
3040             # (ref https://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap2/qtff2.html)
3041             %Image::ExifTool::QuickTime::TrackAperture = (
3042             PROCESS_PROC => \&ProcessMOV,
3043             GROUPS => { 1 => 'Track#', 2 => 'Video' },
3044             clef => {
3045             Name => 'CleanApertureDimensions',
3046             Format => 'fixed32u',
3047             Count => 3,
3048             ValueConv => '$val =~ s/^.*? //; $val', # remove flags word
3049             PrintConv => '$val =~ tr/ /x/; $val',
3050             },
3051             prof => {
3052             Name => 'ProductionApertureDimensions',
3053             Format => 'fixed32u',
3054             Count => 3,
3055             ValueConv => '$val =~ s/^.*? //; $val',
3056             PrintConv => '$val =~ tr/ /x/; $val',
3057             },
3058             enof => {
3059             Name => 'EncodedPixelsDimensions',
3060             Format => 'fixed32u',
3061             Count => 3,
3062             ValueConv => '$val =~ s/^.*? //; $val',
3063             PrintConv => '$val =~ tr/ /x/; $val',
3064             },
3065             );
3066              
3067             # item list atoms
3068             # -> these atoms are unique, and contain one or more 'data' atoms
3069             %Image::ExifTool::QuickTime::ItemList = (
3070             PROCESS_PROC => \&ProcessMOV,
3071             WRITE_PROC => \&WriteQuickTime,
3072             CHECK_PROC => \&CheckQTValue,
3073             WRITABLE => 1,
3074             PREFERRED => 2, # (preferred over UserData and Keys tags when writing)
3075             FORMAT => 'string',
3076             GROUPS => { 1 => 'ItemList', 2 => 'Audio' },
3077             WRITE_GROUP => 'ItemList',
3078             LANG_INFO => \&GetLangInfo,
3079             NOTES => q{
3080             This is the preferred location for creating new QuickTime tags. Tags in
3081             this table support alternate languages which are accessed by adding a
3082             3-character ISO 639-2 language code and an optional ISO 3166-1 alpha 2
3083             country code to the tag name (eg. "ItemList:Title-fra" or
3084             "ItemList::Title-fra-FR"). When creating a new Meta box to contain the
3085             ItemList directory, by default ExifTool adds an 'mdir' (Metadata) Handler
3086             box because Apple software may ignore ItemList tags otherwise, but the API
3087             L option may be set to 0 to avoid this.
3088             },
3089             # in this table, binary 1 and 2-byte "data"-type tags are interpreted as
3090             # int8u and int16u. Multi-byte binary "data" tags are extracted as binary data.
3091             # (Note that the Preferred property is set to 0 for some tags to prevent them
3092             # from being created when a same-named tag already exists in the table)
3093             "\xa9ART" => 'Artist',
3094             "\xa9alb" => 'Album',
3095             "\xa9aut" => { Name => 'Author', Avoid => 1, Groups => { 2 => 'Author' } }, #forum10091 ('auth' is preferred)
3096             "\xa9cmt" => 'Comment',
3097             "\xa9com" => { Name => 'Composer', Avoid => 1, }, # ("\xa9wrt" is preferred in ItemList)
3098             "\xa9day" => {
3099             Name => 'ContentCreateDate',
3100             Groups => { 2 => 'Time' },
3101             Shift => 'Time',
3102             # handle values in the form "2010-02-12T13:27:14-0800"
3103             ValueConv => q{
3104             require Image::ExifTool::XMP;
3105             $val = Image::ExifTool::XMP::ConvertXMPDate($val);
3106             $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
3107             return $val;
3108             },
3109             ValueConvInv => q{
3110             require Image::ExifTool::XMP;
3111             $val = Image::ExifTool::XMP::FormatXMPDate($val);
3112             $val =~ s/([-+]\d{2}):(\d{2})$/$1$2/; # remove time zone colon
3113             return $val;
3114             },
3115             PrintConv => '$self->ConvertDateTime($val)',
3116             PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
3117             },
3118             "\xa9des" => 'Description', #4
3119             "\xa9enc" => 'EncodedBy', #10
3120             "\xa9gen" => 'Genre',
3121             "\xa9grp" => 'Grouping',
3122             "\xa9lyr" => 'Lyrics',
3123             "\xa9nam" => 'Title',
3124             "\xa9too" => 'Encoder',
3125             "\xa9trk" => 'Track',
3126             "\xa9wrt" => 'Composer',
3127             #
3128             # the following tags written by AtomicParsley 0.9.6
3129             # (ref https://exiftool.org/forum/index.php?topic=11455.0)
3130             #
3131             "\xa9st3" => 'Subtitle',
3132             "\xa9con" => 'Conductor',
3133             "\xa9sol" => 'Soloist',
3134             "\xa9arg" => 'Arranger',
3135             "\xa9ope" => 'OriginalArtist',
3136             "\xa9dir" => 'Director',
3137             "\xa9ard" => 'ArtDirector',
3138             "\xa9sne" => 'SoundEngineer',
3139             "\xa9prd" => 'Producer',
3140             "\xa9xpd" => 'ExecutiveProducer',
3141             sdes => 'StoreDescription',
3142             #
3143             '----' => {
3144             Name => 'iTunesInfo',
3145             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::iTunesInfo' },
3146             },
3147             aART => { Name => 'AlbumArtist', Groups => { 2 => 'Author' } },
3148             covr => { Name => 'CoverArt', Groups => { 2 => 'Preview' } },
3149             cpil => { #10
3150             Name => 'Compilation',
3151             Format => 'int8u', #27 (ref 23 contradicts what AtomicParsley actually writes, which is int8s)
3152             Writable => 'int8s',
3153             PrintConv => { 0 => 'No', 1 => 'Yes' },
3154             },
3155             disk => {
3156             Name => 'DiskNumber',
3157             Format => 'undef', # (necessary to prevent decoding as string!)
3158             ValueConv => q{
3159             return \$val unless length($val) >= 6;
3160             my @a = unpack 'x2nn', $val;
3161             return $a[1] ? join(' of ', @a) : $a[0];
3162             },
3163             ValueConvInv => q{
3164             my @a = $val =~ /\d+/g;
3165             return undef if @a == 0 or @a > 2;
3166             push @a, 0 if @a == 1;
3167             return pack('n3', 0, @a);
3168             },
3169             },
3170             pgap => { #10
3171             Name => 'PlayGap',
3172             Format => 'int8u', #23
3173             Writable => 'int8s', #27
3174             PrintConv => {
3175             0 => 'Insert Gap',
3176             1 => 'No Gap',
3177             },
3178             },
3179             tmpo => {
3180             Name => 'BeatsPerMinute',
3181             # marked as boolean but really int16u in my sample
3182             # (but written as int16s by iTunes and AtomicParsley, ref forum11506)
3183             Format => 'int16u',
3184             Writable => 'int16s',
3185             },
3186             trkn => {
3187             Name => 'TrackNumber',
3188             Format => 'undef', # (necessary to prevent decoding as string!)
3189             ValueConv => q{
3190             return \$val unless length($val) >= 6;
3191             my @a = unpack 'x2nn', $val;
3192             return $a[1] ? join(' of ', @a) : $a[0];
3193             },
3194             # (see forum11501 for discussion about the format used)
3195             ValueConvInv => q{
3196             my @a = $val =~ /\d+/g;
3197             return undef if @a == 0 or @a > 2;
3198             push @a, 0 if @a == 1;
3199             return pack('n4', 0, @a, 0);
3200             },
3201             },
3202             #
3203             # Note: it is possible that the tags below are not being decoded properly
3204             # because I don't have samples to verify many of these - PH
3205             #
3206             akID => { #10
3207             Name => 'AppleStoreAccountType',
3208             Format => 'int8u', #24
3209             Writable => 'int8s', #27
3210             PrintConv => {
3211             0 => 'iTunes',
3212             1 => 'AOL',
3213             },
3214             },
3215             albm => { Name => 'Album', Avoid => 1 }, #(ffmpeg source)
3216             apID => 'AppleStoreAccount',
3217             atID => { #10 (or TV series)
3218             Name => 'AlbumTitleID',
3219             Format => 'int32u',
3220             Writable => 'int32s', #27
3221             },
3222             auth => { Name => 'Author', Groups => { 2 => 'Author' } },
3223             catg => 'Category', #7
3224             cnID => { #10
3225             Name => 'AppleStoreCatalogID',
3226             Format => 'int32u',
3227             Writable => 'int32s', #27
3228             },
3229             cprt => { Name => 'Copyright', Groups => { 2 => 'Author' } },
3230             dscp => { Name => 'Description', Avoid => 1 },
3231             desc => { Name => 'Description', Avoid => 1 }, #7
3232             gnre => { #10
3233             Name => 'Genre',
3234             Avoid => 1,
3235             # (Note: see https://exiftool.org/forum/index.php?topic=11537.0)
3236             Format => 'undef',
3237             ValueConv => 'unpack("n",$val)',
3238             ValueConvInv => '$val =~ /^\d+$/ ? pack("n",$val) : undef',
3239             PrintConv => q{
3240             return $val unless $val =~ /^\d+$/;
3241             require Image::ExifTool::ID3;
3242             Image::ExifTool::ID3::PrintGenre($val - 1); # note the "- 1"
3243             },
3244             PrintConvInv => q{
3245             return $val if $val =~ /^[0-9]+$/;
3246             require Image::ExifTool::ID3;
3247             my $id = Image::ExifTool::ID3::GetGenreID($val);
3248             return unless defined $id and $id =~ /^\d+$/;
3249             return $id + 1;
3250             },
3251             },
3252             egid => 'EpisodeGlobalUniqueID', #7
3253             geID => { #10
3254             Name => 'GenreID',
3255             Format => 'int32u',
3256             Writable => 'int32s', #27
3257             SeparateTable => 1,
3258             # the following lookup is based on http://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/genres
3259             # (see scripts/parse_genre to parse genre JSON file from above)
3260             PrintConv => { #21/PH
3261             2 => 'Music|Blues',
3262             3 => 'Music|Comedy',
3263             4 => "Music|Children's Music",
3264             5 => 'Music|Classical',
3265             6 => 'Music|Country',
3266             7 => 'Music|Electronic',
3267             8 => 'Music|Holiday',
3268             9 => 'Music|Classical|Opera',
3269             10 => 'Music|Singer/Songwriter',
3270             11 => 'Music|Jazz',
3271             12 => 'Music|Latino',
3272             13 => 'Music|New Age',
3273             14 => 'Music|Pop',
3274             15 => 'Music|R&B/Soul',
3275             16 => 'Music|Soundtrack',
3276             17 => 'Music|Dance',
3277             18 => 'Music|Hip-Hop/Rap',
3278             19 => 'Music|World',
3279             20 => 'Music|Alternative',
3280             21 => 'Music|Rock',
3281             22 => 'Music|Christian & Gospel',
3282             23 => 'Music|Vocal',
3283             24 => 'Music|Reggae',
3284             25 => 'Music|Easy Listening',
3285             26 => 'Podcasts',
3286             27 => 'Music|J-Pop',
3287             28 => 'Music|Enka',
3288             29 => 'Music|Anime',
3289             30 => 'Music|Kayokyoku',
3290             31 => 'Music Videos',
3291             32 => 'TV Shows',
3292             33 => 'Movies',
3293             34 => 'Music',
3294             35 => 'iPod Games',
3295             36 => 'App Store',
3296             37 => 'Tones',
3297             38 => 'Books',
3298             39 => 'Mac App Store',
3299             40 => 'Textbooks',
3300             50 => 'Music|Fitness & Workout',
3301             51 => 'Music|Pop|K-Pop',
3302             52 => 'Music|Karaoke',
3303             53 => 'Music|Instrumental',
3304             74 => 'Audiobooks|News',
3305             75 => 'Audiobooks|Programs & Performances',
3306             500 => 'Fitness Music',
3307             501 => 'Fitness Music|Pop',
3308             502 => 'Fitness Music|Dance',
3309             503 => 'Fitness Music|Hip-Hop',
3310             504 => 'Fitness Music|Rock',
3311             505 => 'Fitness Music|Alt/Indie',
3312             506 => 'Fitness Music|Latino',
3313             507 => 'Fitness Music|Country',
3314             508 => 'Fitness Music|World',
3315             509 => 'Fitness Music|New Age',
3316             510 => 'Fitness Music|Classical',
3317             1001 => 'Music|Alternative|College Rock',
3318             1002 => 'Music|Alternative|Goth Rock',
3319             1003 => 'Music|Alternative|Grunge',
3320             1004 => 'Music|Alternative|Indie Rock',
3321             1005 => 'Music|Alternative|New Wave',
3322             1006 => 'Music|Alternative|Punk',
3323             1007 => 'Music|Blues|Chicago Blues',
3324             1009 => 'Music|Blues|Classic Blues',
3325             1010 => 'Music|Blues|Contemporary Blues',
3326             1011 => 'Music|Blues|Country Blues',
3327             1012 => 'Music|Blues|Delta Blues',
3328             1013 => 'Music|Blues|Electric Blues',
3329             1014 => "Music|Children's Music|Lullabies",
3330             1015 => "Music|Children's Music|Sing-Along",
3331             1016 => "Music|Children's Music|Stories",
3332             1017 => 'Music|Classical|Avant-Garde',
3333             1018 => 'Music|Classical|Baroque Era',
3334             1019 => 'Music|Classical|Chamber Music',
3335             1020 => 'Music|Classical|Chant',
3336             1021 => 'Music|Classical|Choral',
3337             1022 => 'Music|Classical|Classical Crossover',
3338             1023 => 'Music|Classical|Early Music',
3339             1024 => 'Music|Classical|Impressionist',
3340             1025 => 'Music|Classical|Medieval Era',
3341             1026 => 'Music|Classical|Minimalism',
3342             1027 => 'Music|Classical|Modern Era',
3343             1028 => 'Music|Classical|Opera',
3344             1029 => 'Music|Classical|Orchestral',
3345             1030 => 'Music|Classical|Renaissance',
3346             1031 => 'Music|Classical|Romantic Era',
3347             1032 => 'Music|Classical|Wedding Music',
3348             1033 => 'Music|Country|Alternative Country',
3349             1034 => 'Music|Country|Americana',
3350             1035 => 'Music|Country|Bluegrass',
3351             1036 => 'Music|Country|Contemporary Bluegrass',
3352             1037 => 'Music|Country|Contemporary Country',
3353             1038 => 'Music|Country|Country Gospel',
3354             1039 => 'Music|Country|Honky Tonk',
3355             1040 => 'Music|Country|Outlaw Country',
3356             1041 => 'Music|Country|Traditional Bluegrass',
3357             1042 => 'Music|Country|Traditional Country',
3358             1043 => 'Music|Country|Urban Cowboy',
3359             1044 => 'Music|Dance|Breakbeat',
3360             1045 => 'Music|Dance|Exercise',
3361             1046 => 'Music|Dance|Garage',
3362             1047 => 'Music|Dance|Hardcore',
3363             1048 => 'Music|Dance|House',
3364             1049 => "Music|Dance|Jungle/Drum'n'bass",
3365             1050 => 'Music|Dance|Techno',
3366             1051 => 'Music|Dance|Trance',
3367             1052 => 'Music|Jazz|Big Band',
3368             1053 => 'Music|Jazz|Bop',
3369             1054 => 'Music|Easy Listening|Lounge',
3370             1055 => 'Music|Easy Listening|Swing',
3371             1056 => 'Music|Electronic|Ambient',
3372             1057 => 'Music|Electronic|Downtempo',
3373             1058 => 'Music|Electronic|Electronica',
3374             1060 => 'Music|Electronic|IDM/Experimental',
3375             1061 => 'Music|Electronic|Industrial',
3376             1062 => 'Music|Singer/Songwriter|Alternative Folk',
3377             1063 => 'Music|Singer/Songwriter|Contemporary Folk',
3378             1064 => 'Music|Singer/Songwriter|Contemporary Singer/Songwriter',
3379             1065 => 'Music|Singer/Songwriter|Folk-Rock',
3380             1066 => 'Music|Singer/Songwriter|New Acoustic',
3381             1067 => 'Music|Singer/Songwriter|Traditional Folk',
3382             1068 => 'Music|Hip-Hop/Rap|Alternative Rap',
3383             1069 => 'Music|Hip-Hop/Rap|Dirty South',
3384             1070 => 'Music|Hip-Hop/Rap|East Coast Rap',
3385             1071 => 'Music|Hip-Hop/Rap|Gangsta Rap',
3386             1072 => 'Music|Hip-Hop/Rap|Hardcore Rap',
3387             1073 => 'Music|Hip-Hop/Rap|Hip-Hop',
3388             1074 => 'Music|Hip-Hop/Rap|Latin Rap',
3389             1075 => 'Music|Hip-Hop/Rap|Old School Rap',
3390             1076 => 'Music|Hip-Hop/Rap|Rap',
3391             1077 => 'Music|Hip-Hop/Rap|Underground Rap',
3392             1078 => 'Music|Hip-Hop/Rap|West Coast Rap',
3393             1079 => 'Music|Holiday|Chanukah',
3394             1080 => 'Music|Holiday|Christmas',
3395             1081 => "Music|Holiday|Christmas: Children's",
3396             1082 => 'Music|Holiday|Christmas: Classic',
3397             1083 => 'Music|Holiday|Christmas: Classical',
3398             1084 => 'Music|Holiday|Christmas: Jazz',
3399             1085 => 'Music|Holiday|Christmas: Modern',
3400             1086 => 'Music|Holiday|Christmas: Pop',
3401             1087 => 'Music|Holiday|Christmas: R&B',
3402             1088 => 'Music|Holiday|Christmas: Religious',
3403             1089 => 'Music|Holiday|Christmas: Rock',
3404             1090 => 'Music|Holiday|Easter',
3405             1091 => 'Music|Holiday|Halloween',
3406             1092 => 'Music|Holiday|Holiday: Other',
3407             1093 => 'Music|Holiday|Thanksgiving',
3408             1094 => 'Music|Christian & Gospel|CCM',
3409             1095 => 'Music|Christian & Gospel|Christian Metal',
3410             1096 => 'Music|Christian & Gospel|Christian Pop',
3411             1097 => 'Music|Christian & Gospel|Christian Rap',
3412             1098 => 'Music|Christian & Gospel|Christian Rock',
3413             1099 => 'Music|Christian & Gospel|Classic Christian',
3414             1100 => 'Music|Christian & Gospel|Contemporary Gospel',
3415             1101 => 'Music|Christian & Gospel|Gospel',
3416             1103 => 'Music|Christian & Gospel|Praise & Worship',
3417             1104 => 'Music|Christian & Gospel|Southern Gospel',
3418             1105 => 'Music|Christian & Gospel|Traditional Gospel',
3419             1106 => 'Music|Jazz|Avant-Garde Jazz',
3420             1107 => 'Music|Jazz|Contemporary Jazz',
3421             1108 => 'Music|Jazz|Crossover Jazz',
3422             1109 => 'Music|Jazz|Dixieland',
3423             1110 => 'Music|Jazz|Fusion',
3424             1111 => 'Music|Jazz|Latin Jazz',
3425             1112 => 'Music|Jazz|Mainstream Jazz',
3426             1113 => 'Music|Jazz|Ragtime',
3427             1114 => 'Music|Jazz|Smooth Jazz',
3428             1115 => 'Music|Latino|Latin Jazz',
3429             1116 => 'Music|Latino|Contemporary Latin',
3430             1117 => 'Music|Latino|Pop Latino',
3431             1118 => 'Music|Latino|Raices', # (Raíces)
3432             1119 => 'Music|Latino|Urbano latino',
3433             1120 => 'Music|Latino|Baladas y Boleros',
3434             1121 => 'Music|Latino|Rock y Alternativo',
3435             1122 => 'Music|Brazilian',
3436             1123 => 'Music|Latino|Musica Mexicana', # (Música Mexicana)
3437             1124 => 'Music|Latino|Musica tropical', # (Música tropical)
3438             1125 => 'Music|New Age|Environmental',
3439             1126 => 'Music|New Age|Healing',
3440             1127 => 'Music|New Age|Meditation',
3441             1128 => 'Music|New Age|Nature',
3442             1129 => 'Music|New Age|Relaxation',
3443             1130 => 'Music|New Age|Travel',
3444             1131 => 'Music|Pop|Adult Contemporary',
3445             1132 => 'Music|Pop|Britpop',
3446             1133 => 'Music|Pop|Pop/Rock',
3447             1134 => 'Music|Pop|Soft Rock',
3448             1135 => 'Music|Pop|Teen Pop',
3449             1136 => 'Music|R&B/Soul|Contemporary R&B',
3450             1137 => 'Music|R&B/Soul|Disco',
3451             1138 => 'Music|R&B/Soul|Doo Wop',
3452             1139 => 'Music|R&B/Soul|Funk',
3453             1140 => 'Music|R&B/Soul|Motown',
3454             1141 => 'Music|R&B/Soul|Neo-Soul',
3455             1142 => 'Music|R&B/Soul|Quiet Storm',
3456             1143 => 'Music|R&B/Soul|Soul',
3457             1144 => 'Music|Rock|Adult Alternative',
3458             1145 => 'Music|Rock|American Trad Rock',
3459             1146 => 'Music|Rock|Arena Rock',
3460             1147 => 'Music|Rock|Blues-Rock',
3461             1148 => 'Music|Rock|British Invasion',
3462             1149 => 'Music|Rock|Death Metal/Black Metal',
3463             1150 => 'Music|Rock|Glam Rock',
3464             1151 => 'Music|Rock|Hair Metal',
3465             1152 => 'Music|Rock|Hard Rock',
3466             1153 => 'Music|Rock|Metal',
3467             1154 => 'Music|Rock|Jam Bands',
3468             1155 => 'Music|Rock|Prog-Rock/Art Rock',
3469             1156 => 'Music|Rock|Psychedelic',
3470             1157 => 'Music|Rock|Rock & Roll',
3471             1158 => 'Music|Rock|Rockabilly',
3472             1159 => 'Music|Rock|Roots Rock',
3473             1160 => 'Music|Rock|Singer/Songwriter',
3474             1161 => 'Music|Rock|Southern Rock',
3475             1162 => 'Music|Rock|Surf',
3476             1163 => 'Music|Rock|Tex-Mex',
3477             1165 => 'Music|Soundtrack|Foreign Cinema',
3478             1166 => 'Music|Soundtrack|Musicals',
3479             1167 => 'Music|Comedy|Novelty',
3480             1168 => 'Music|Soundtrack|Original Score',
3481             1169 => 'Music|Soundtrack|Soundtrack',
3482             1171 => 'Music|Comedy|Standup Comedy',
3483             1172 => 'Music|Soundtrack|TV Soundtrack',
3484             1173 => 'Music|Vocal|Standards',
3485             1174 => 'Music|Vocal|Traditional Pop',
3486             1175 => 'Music|Jazz|Vocal Jazz',
3487             1176 => 'Music|Vocal|Vocal Pop',
3488             1177 => 'Music|African|Afro-Beat',
3489             1178 => 'Music|African|Afro-Pop',
3490             1179 => 'Music|World|Cajun',
3491             1180 => 'Music|World|Celtic',
3492             1181 => 'Music|World|Celtic Folk',
3493             1182 => 'Music|World|Contemporary Celtic',
3494             1183 => 'Music|Reggae|Modern Dancehall',
3495             1184 => 'Music|World|Drinking Songs',
3496             1185 => 'Music|Indian|Indian Pop',
3497             1186 => 'Music|World|Japanese Pop',
3498             1187 => 'Music|World|Klezmer',
3499             1188 => 'Music|World|Polka',
3500             1189 => 'Music|World|Traditional Celtic',
3501             1190 => 'Music|World|Worldbeat',
3502             1191 => 'Music|World|Zydeco',
3503             1192 => 'Music|Reggae|Roots Reggae',
3504             1193 => 'Music|Reggae|Dub',
3505             1194 => 'Music|Reggae|Ska',
3506             1195 => 'Music|World|Caribbean',
3507             1196 => 'Music|World|South America',
3508             1197 => 'Music|Arabic',
3509             1198 => 'Music|World|North America',
3510             1199 => 'Music|World|Hawaii',
3511             1200 => 'Music|World|Australia',
3512             1201 => 'Music|World|Japan',
3513             1202 => 'Music|World|France',
3514             1203 => 'Music|African',
3515             1204 => 'Music|World|Asia',
3516             1205 => 'Music|World|Europe',
3517             1206 => 'Music|World|South Africa',
3518             1207 => 'Music|Jazz|Hard Bop',
3519             1208 => 'Music|Jazz|Trad Jazz',
3520             1209 => 'Music|Jazz|Cool Jazz',
3521             1210 => 'Music|Blues|Acoustic Blues',
3522             1211 => 'Music|Classical|High Classical',
3523             1220 => 'Music|Brazilian|Axe', # (Axé)
3524             1221 => 'Music|Brazilian|Bossa Nova',
3525             1222 => 'Music|Brazilian|Choro',
3526             1223 => 'Music|Brazilian|Forro', # (Forró)
3527             1224 => 'Music|Brazilian|Frevo',
3528             1225 => 'Music|Brazilian|MPB',
3529             1226 => 'Music|Brazilian|Pagode',
3530             1227 => 'Music|Brazilian|Samba',
3531             1228 => 'Music|Brazilian|Sertanejo',
3532             1229 => 'Music|Brazilian|Baile Funk',
3533             1230 => 'Music|Alternative|Chinese Alt',
3534             1231 => 'Music|Alternative|Korean Indie',
3535             1232 => 'Music|Chinese',
3536             1233 => 'Music|Chinese|Chinese Classical',
3537             1234 => 'Music|Chinese|Chinese Flute',
3538             1235 => 'Music|Chinese|Chinese Opera',
3539             1236 => 'Music|Chinese|Chinese Orchestral',
3540             1237 => 'Music|Chinese|Chinese Regional Folk',
3541             1238 => 'Music|Chinese|Chinese Strings',
3542             1239 => 'Music|Chinese|Taiwanese Folk',
3543             1240 => 'Music|Chinese|Tibetan Native Music',
3544             1241 => 'Music|Hip-Hop/Rap|Chinese Hip-Hop',
3545             1242 => 'Music|Hip-Hop/Rap|Korean Hip-Hop',
3546             1243 => 'Music|Korean',
3547             1244 => 'Music|Korean|Korean Classical',
3548             1245 => 'Music|Korean|Korean Trad Song',
3549             1246 => 'Music|Korean|Korean Trad Instrumental',
3550             1247 => 'Music|Korean|Korean Trad Theater',
3551             1248 => 'Music|Rock|Chinese Rock',
3552             1249 => 'Music|Rock|Korean Rock',
3553             1250 => 'Music|Pop|C-Pop',
3554             1251 => 'Music|Pop|Cantopop/HK-Pop',
3555             1252 => 'Music|Pop|Korean Folk-Pop',
3556             1253 => 'Music|Pop|Mandopop',
3557             1254 => 'Music|Pop|Tai-Pop',
3558             1255 => 'Music|Pop|Malaysian Pop',
3559             1256 => 'Music|Pop|Pinoy Pop',
3560             1257 => 'Music|Pop|Original Pilipino Music',
3561             1258 => 'Music|Pop|Manilla Sound',
3562             1259 => 'Music|Pop|Indo Pop',
3563             1260 => 'Music|Pop|Thai Pop',
3564             1261 => 'Music|Vocal|Trot',
3565             1262 => 'Music|Indian',
3566             1263 => 'Music|Indian|Bollywood',
3567             1264 => 'Music|Indian|Regional Indian|Tamil',
3568             1265 => 'Music|Indian|Regional Indian|Telugu',
3569             1266 => 'Music|Indian|Regional Indian',
3570             1267 => 'Music|Indian|Devotional & Spiritual',
3571             1268 => 'Music|Indian|Sufi',
3572             1269 => 'Music|Indian|Indian Classical',
3573             1270 => 'Music|Russian|Russian Chanson',
3574             1271 => 'Music|World|Dini',
3575             1272 => 'Music|Turkish|Halk',
3576             1273 => 'Music|Turkish|Sanat',
3577             1274 => 'Music|World|Dangdut',
3578             1275 => 'Music|World|Indonesian Religious',
3579             1276 => 'Music|World|Calypso',
3580             1277 => 'Music|World|Soca',
3581             1278 => 'Music|Indian|Ghazals',
3582             1279 => 'Music|Indian|Indian Folk',
3583             1280 => 'Music|Turkish|Arabesque',
3584             1281 => 'Music|African|Afrikaans',
3585             1282 => 'Music|World|Farsi',
3586             1283 => 'Music|World|Israeli',
3587             1284 => 'Music|Arabic|Khaleeji',
3588             1285 => 'Music|Arabic|North African',
3589             1286 => 'Music|Arabic|Arabic Pop',
3590             1287 => 'Music|Arabic|Islamic',
3591             1288 => 'Music|Soundtrack|Sound Effects',
3592             1289 => 'Music|Folk',
3593             1290 => 'Music|Orchestral',
3594             1291 => 'Music|Marching',
3595             1293 => 'Music|Pop|Oldies',
3596             1294 => 'Music|Country|Thai Country',
3597             1295 => 'Music|World|Flamenco',
3598             1296 => 'Music|World|Tango',
3599             1297 => 'Music|World|Fado',
3600             1298 => 'Music|World|Iberia',
3601             1299 => 'Music|Russian',
3602             1300 => 'Music|Turkish',
3603             1301 => 'Podcasts|Arts',
3604             1302 => 'Podcasts|Society & Culture|Personal Journals',
3605             1303 => 'Podcasts|Comedy',
3606             1304 => 'Podcasts|Education',
3607             1305 => 'Podcasts|Kids & Family',
3608             1306 => 'Podcasts|Arts|Food',
3609             1307 => 'Podcasts|Health',
3610             1309 => 'Podcasts|TV & Film',
3611             1310 => 'Podcasts|Music',
3612             1311 => 'Podcasts|News & Politics',
3613             1314 => 'Podcasts|Religion & Spirituality',
3614             1315 => 'Podcasts|Science & Medicine',
3615             1316 => 'Podcasts|Sports & Recreation',
3616             1318 => 'Podcasts|Technology',
3617             1320 => 'Podcasts|Society & Culture|Places & Travel',
3618             1321 => 'Podcasts|Business',
3619             1323 => 'Podcasts|Games & Hobbies',
3620             1324 => 'Podcasts|Society & Culture',
3621             1325 => 'Podcasts|Government & Organizations',
3622             1337 => 'Music Videos|Classical|Piano',
3623             1401 => 'Podcasts|Arts|Literature',
3624             1402 => 'Podcasts|Arts|Design',
3625             1404 => 'Podcasts|Games & Hobbies|Video Games',
3626             1405 => 'Podcasts|Arts|Performing Arts',
3627             1406 => 'Podcasts|Arts|Visual Arts',
3628             1410 => 'Podcasts|Business|Careers',
3629             1412 => 'Podcasts|Business|Investing',
3630             1413 => 'Podcasts|Business|Management & Marketing',
3631             1415 => 'Podcasts|Education|K-12',
3632             1416 => 'Podcasts|Education|Higher Education',
3633             1417 => 'Podcasts|Health|Fitness & Nutrition',
3634             1420 => 'Podcasts|Health|Self-Help',
3635             1421 => 'Podcasts|Health|Sexuality',
3636             1438 => 'Podcasts|Religion & Spirituality|Buddhism',
3637             1439 => 'Podcasts|Religion & Spirituality|Christianity',
3638             1440 => 'Podcasts|Religion & Spirituality|Islam',
3639             1441 => 'Podcasts|Religion & Spirituality|Judaism',
3640             1443 => 'Podcasts|Society & Culture|Philosophy',
3641             1444 => 'Podcasts|Religion & Spirituality|Spirituality',
3642             1446 => 'Podcasts|Technology|Gadgets',
3643             1448 => 'Podcasts|Technology|Tech News',
3644             1450 => 'Podcasts|Technology|Podcasting',
3645             1454 => 'Podcasts|Games & Hobbies|Automotive',
3646             1455 => 'Podcasts|Games & Hobbies|Aviation',
3647             1456 => 'Podcasts|Sports & Recreation|Outdoor',
3648             1459 => 'Podcasts|Arts|Fashion & Beauty',
3649             1460 => 'Podcasts|Games & Hobbies|Hobbies',
3650             1461 => 'Podcasts|Games & Hobbies|Other Games',
3651             1462 => 'Podcasts|Society & Culture|History',
3652             1463 => 'Podcasts|Religion & Spirituality|Hinduism',
3653             1464 => 'Podcasts|Religion & Spirituality|Other',
3654             1465 => 'Podcasts|Sports & Recreation|Professional',
3655             1466 => 'Podcasts|Sports & Recreation|College & High School',
3656             1467 => 'Podcasts|Sports & Recreation|Amateur',
3657             1468 => 'Podcasts|Education|Educational Technology',
3658             1469 => 'Podcasts|Education|Language Courses',
3659             1470 => 'Podcasts|Education|Training',
3660             1471 => 'Podcasts|Business|Business News',
3661             1472 => 'Podcasts|Business|Shopping',
3662             1473 => 'Podcasts|Government & Organizations|National',
3663             1474 => 'Podcasts|Government & Organizations|Regional',
3664             1475 => 'Podcasts|Government & Organizations|Local',
3665             1476 => 'Podcasts|Government & Organizations|Non-Profit',
3666             1477 => 'Podcasts|Science & Medicine|Natural Sciences',
3667             1478 => 'Podcasts|Science & Medicine|Medicine',
3668             1479 => 'Podcasts|Science & Medicine|Social Sciences',
3669             1480 => 'Podcasts|Technology|Software How-To',
3670             1481 => 'Podcasts|Health|Alternative Health',
3671             1482 => 'Podcasts|Arts|Books',
3672             1483 => 'Podcasts|Fiction',
3673             1484 => 'Podcasts|Fiction|Drama',
3674             1485 => 'Podcasts|Fiction|Science Fiction',
3675             1486 => 'Podcasts|Fiction|Comedy Fiction',
3676             1487 => 'Podcasts|History',
3677             1488 => 'Podcasts|True Crime',
3678             1489 => 'Podcasts|News',
3679             1490 => 'Podcasts|News|Business News',
3680             1491 => 'Podcasts|Business|Management',
3681             1492 => 'Podcasts|Business|Marketing',
3682             1493 => 'Podcasts|Business|Entrepreneurship',
3683             1494 => 'Podcasts|Business|Non-Profit',
3684             1495 => 'Podcasts|Comedy|Improv',
3685             1496 => 'Podcasts|Comedy|Comedy Interviews',
3686             1497 => 'Podcasts|Comedy|Stand-Up',
3687             1498 => 'Podcasts|Education|Language Learning',
3688             1499 => 'Podcasts|Education|How To',
3689             1500 => 'Podcasts|Education|Self-Improvement',
3690             1501 => 'Podcasts|Education|Courses',
3691             1502 => 'Podcasts|Leisure',
3692             1503 => 'Podcasts|Leisure|Automotive',
3693             1504 => 'Podcasts|Leisure|Aviation',
3694             1505 => 'Podcasts|Leisure|Hobbies',
3695             1506 => 'Podcasts|Leisure|Crafts',
3696             1507 => 'Podcasts|Leisure|Games',
3697             1508 => 'Podcasts|Leisure|Home & Garden',
3698             1509 => 'Podcasts|Leisure|Video Games',
3699             1510 => 'Podcasts|Leisure|Animation & Manga',
3700             1511 => 'Podcasts|Government',
3701             1512 => 'Podcasts|Health & Fitness',
3702             1513 => 'Podcasts|Health & Fitness|Alternative Health',
3703             1514 => 'Podcasts|Health & Fitness|Fitness',
3704             1515 => 'Podcasts|Health & Fitness|Nutrition',
3705             1516 => 'Podcasts|Health & Fitness|Sexuality',
3706             1517 => 'Podcasts|Health & Fitness|Mental Health',
3707             1518 => 'Podcasts|Health & Fitness|Medicine',
3708             1519 => 'Podcasts|Kids & Family|Education for Kids',
3709             1520 => 'Podcasts|Kids & Family|Stories for Kids',
3710             1521 => 'Podcasts|Kids & Family|Parenting',
3711             1522 => 'Podcasts|Kids & Family|Pets & Animals',
3712             1523 => 'Podcasts|Music|Music Commentary',
3713             1524 => 'Podcasts|Music|Music History',
3714             1525 => 'Podcasts|Music|Music Interviews',
3715             1526 => 'Podcasts|News|Daily News',
3716             1527 => 'Podcasts|News|Politics',
3717             1528 => 'Podcasts|News|Tech News',
3718             1529 => 'Podcasts|News|Sports News',
3719             1530 => 'Podcasts|News|News Commentary',
3720             1531 => 'Podcasts|News|Entertainment News',
3721             1532 => 'Podcasts|Religion & Spirituality|Religion',
3722             1533 => 'Podcasts|Science',
3723             1534 => 'Podcasts|Science|Natural Sciences',
3724             1535 => 'Podcasts|Science|Social Sciences',
3725             1536 => 'Podcasts|Science|Mathematics',
3726             1537 => 'Podcasts|Science|Nature',
3727             1538 => 'Podcasts|Science|Astronomy',
3728             1539 => 'Podcasts|Science|Chemistry',
3729             1540 => 'Podcasts|Science|Earth Sciences',
3730             1541 => 'Podcasts|Science|Life Sciences',
3731             1542 => 'Podcasts|Science|Physics',
3732             1543 => 'Podcasts|Society & Culture|Documentary',
3733             1544 => 'Podcasts|Society & Culture|Relationships',
3734             1545 => 'Podcasts|Sports',
3735             1546 => 'Podcasts|Sports|Soccer',
3736             1547 => 'Podcasts|Sports|Football',
3737             1548 => 'Podcasts|Sports|Basketball',
3738             1549 => 'Podcasts|Sports|Baseball',
3739             1550 => 'Podcasts|Sports|Hockey',
3740             1551 => 'Podcasts|Sports|Running',
3741             1552 => 'Podcasts|Sports|Rugby',
3742             1553 => 'Podcasts|Sports|Golf',
3743             1554 => 'Podcasts|Sports|Cricket',
3744             1555 => 'Podcasts|Sports|Wrestling',
3745             1556 => 'Podcasts|Sports|Tennis',
3746             1557 => 'Podcasts|Sports|Volleyball',
3747             1558 => 'Podcasts|Sports|Swimming',
3748             1559 => 'Podcasts|Sports|Wilderness',
3749             1560 => 'Podcasts|Sports|Fantasy Sports',
3750             1561 => 'Podcasts|TV & Film|TV Reviews',
3751             1562 => 'Podcasts|TV & Film|After Shows',
3752             1563 => 'Podcasts|TV & Film|Film Reviews',
3753             1564 => 'Podcasts|TV & Film|Film History',
3754             1565 => 'Podcasts|TV & Film|Film Interviews',
3755             1602 => 'Music Videos|Blues',
3756             1603 => 'Music Videos|Comedy',
3757             1604 => "Music Videos|Children's Music",
3758             1605 => 'Music Videos|Classical',
3759             1606 => 'Music Videos|Country',
3760             1607 => 'Music Videos|Electronic',
3761             1608 => 'Music Videos|Holiday',
3762             1609 => 'Music Videos|Classical|Opera',
3763             1610 => 'Music Videos|Singer/Songwriter',
3764             1611 => 'Music Videos|Jazz',
3765             1612 => 'Music Videos|Latin',
3766             1613 => 'Music Videos|New Age',
3767             1614 => 'Music Videos|Pop',
3768             1615 => 'Music Videos|R&B/Soul',
3769             1616 => 'Music Videos|Soundtrack',
3770             1617 => 'Music Videos|Dance',
3771             1618 => 'Music Videos|Hip-Hop/Rap',
3772             1619 => 'Music Videos|World',
3773             1620 => 'Music Videos|Alternative',
3774             1621 => 'Music Videos|Rock',
3775             1622 => 'Music Videos|Christian & Gospel',
3776             1623 => 'Music Videos|Vocal',
3777             1624 => 'Music Videos|Reggae',
3778             1625 => 'Music Videos|Easy Listening',
3779             1626 => 'Music Videos|Podcasts',
3780             1627 => 'Music Videos|J-Pop',
3781             1628 => 'Music Videos|Enka',
3782             1629 => 'Music Videos|Anime',
3783             1630 => 'Music Videos|Kayokyoku',
3784             1631 => 'Music Videos|Disney',
3785             1632 => 'Music Videos|French Pop',
3786             1633 => 'Music Videos|German Pop',
3787             1634 => 'Music Videos|German Folk',
3788             1635 => 'Music Videos|Alternative|Chinese Alt',
3789             1636 => 'Music Videos|Alternative|Korean Indie',
3790             1637 => 'Music Videos|Chinese',
3791             1638 => 'Music Videos|Chinese|Chinese Classical',
3792             1639 => 'Music Videos|Chinese|Chinese Flute',
3793             1640 => 'Music Videos|Chinese|Chinese Opera',
3794             1641 => 'Music Videos|Chinese|Chinese Orchestral',
3795             1642 => 'Music Videos|Chinese|Chinese Regional Folk',
3796             1643 => 'Music Videos|Chinese|Chinese Strings',
3797             1644 => 'Music Videos|Chinese|Taiwanese Folk',
3798             1645 => 'Music Videos|Chinese|Tibetan Native Music',
3799             1646 => 'Music Videos|Hip-Hop/Rap|Chinese Hip-Hop',
3800             1647 => 'Music Videos|Hip-Hop/Rap|Korean Hip-Hop',
3801             1648 => 'Music Videos|Korean',
3802             1649 => 'Music Videos|Korean|Korean Classical',
3803             1650 => 'Music Videos|Korean|Korean Trad Song',
3804             1651 => 'Music Videos|Korean|Korean Trad Instrumental',
3805             1652 => 'Music Videos|Korean|Korean Trad Theater',
3806             1653 => 'Music Videos|Rock|Chinese Rock',
3807             1654 => 'Music Videos|Rock|Korean Rock',
3808             1655 => 'Music Videos|Pop|C-Pop',
3809             1656 => 'Music Videos|Pop|Cantopop/HK-Pop',
3810             1657 => 'Music Videos|Pop|Korean Folk-Pop',
3811             1658 => 'Music Videos|Pop|Mandopop',
3812             1659 => 'Music Videos|Pop|Tai-Pop',
3813             1660 => 'Music Videos|Pop|Malaysian Pop',
3814             1661 => 'Music Videos|Pop|Pinoy Pop',
3815             1662 => 'Music Videos|Pop|Original Pilipino Music',
3816             1663 => 'Music Videos|Pop|Manilla Sound',
3817             1664 => 'Music Videos|Pop|Indo Pop',
3818             1665 => 'Music Videos|Pop|Thai Pop',
3819             1666 => 'Music Videos|Vocal|Trot',
3820             1671 => 'Music Videos|Brazilian',
3821             1672 => 'Music Videos|Brazilian|Axe', # (Axé)
3822             1673 => 'Music Videos|Brazilian|Baile Funk',
3823             1674 => 'Music Videos|Brazilian|Bossa Nova',
3824             1675 => 'Music Videos|Brazilian|Choro',
3825             1676 => 'Music Videos|Brazilian|Forro',
3826             1677 => 'Music Videos|Brazilian|Frevo',
3827             1678 => 'Music Videos|Brazilian|MPB',
3828             1679 => 'Music Videos|Brazilian|Pagode',
3829             1680 => 'Music Videos|Brazilian|Samba',
3830             1681 => 'Music Videos|Brazilian|Sertanejo',
3831             1682 => 'Music Videos|Classical|High Classical',
3832             1683 => 'Music Videos|Fitness & Workout',
3833             1684 => 'Music Videos|Instrumental',
3834             1685 => 'Music Videos|Jazz|Big Band',
3835             1686 => 'Music Videos|Pop|K-Pop',
3836             1687 => 'Music Videos|Karaoke',
3837             1688 => 'Music Videos|Rock|Heavy Metal',
3838             1689 => 'Music Videos|Spoken Word',
3839             1690 => 'Music Videos|Indian',
3840             1691 => 'Music Videos|Indian|Bollywood',
3841             1692 => 'Music Videos|Indian|Regional Indian|Tamil',
3842             1693 => 'Music Videos|Indian|Regional Indian|Telugu',
3843             1694 => 'Music Videos|Indian|Regional Indian',
3844             1695 => 'Music Videos|Indian|Devotional & Spiritual',
3845             1696 => 'Music Videos|Indian|Sufi',
3846             1697 => 'Music Videos|Indian|Indian Classical',
3847             1698 => 'Music Videos|Russian|Russian Chanson',
3848             1699 => 'Music Videos|World|Dini',
3849             1700 => 'Music Videos|Turkish|Halk',
3850             1701 => 'Music Videos|Turkish|Sanat',
3851             1702 => 'Music Videos|World|Dangdut',
3852             1703 => 'Music Videos|World|Indonesian Religious',
3853             1704 => 'Music Videos|Indian|Indian Pop',
3854             1705 => 'Music Videos|World|Calypso',
3855             1706 => 'Music Videos|World|Soca',
3856             1707 => 'Music Videos|Indian|Ghazals',
3857             1708 => 'Music Videos|Indian|Indian Folk',
3858             1709 => 'Music Videos|Turkish|Arabesque',
3859             1710 => 'Music Videos|African|Afrikaans',
3860             1711 => 'Music Videos|World|Farsi',
3861             1712 => 'Music Videos|World|Israeli',
3862             1713 => 'Music Videos|Arabic',
3863             1714 => 'Music Videos|Arabic|Khaleeji',
3864             1715 => 'Music Videos|Arabic|North African',
3865             1716 => 'Music Videos|Arabic|Arabic Pop',
3866             1717 => 'Music Videos|Arabic|Islamic',
3867             1718 => 'Music Videos|Soundtrack|Sound Effects',
3868             1719 => 'Music Videos|Folk',
3869             1720 => 'Music Videos|Orchestral',
3870             1721 => 'Music Videos|Marching',
3871             1723 => 'Music Videos|Pop|Oldies',
3872             1724 => 'Music Videos|Country|Thai Country',
3873             1725 => 'Music Videos|World|Flamenco',
3874             1726 => 'Music Videos|World|Tango',
3875             1727 => 'Music Videos|World|Fado',
3876             1728 => 'Music Videos|World|Iberia',
3877             1729 => 'Music Videos|Russian',
3878             1730 => 'Music Videos|Turkish',
3879             1731 => 'Music Videos|Alternative|College Rock',
3880             1732 => 'Music Videos|Alternative|Goth Rock',
3881             1733 => 'Music Videos|Alternative|Grunge',
3882             1734 => 'Music Videos|Alternative|Indie Rock',
3883             1735 => 'Music Videos|Alternative|New Wave',
3884             1736 => 'Music Videos|Alternative|Punk',
3885             1737 => 'Music Videos|Blues|Acoustic Blues',
3886             1738 => 'Music Videos|Blues|Chicago Blues',
3887             1739 => 'Music Videos|Blues|Classic Blues',
3888             1740 => 'Music Videos|Blues|Contemporary Blues',
3889             1741 => 'Music Videos|Blues|Country Blues',
3890             1742 => 'Music Videos|Blues|Delta Blues',
3891             1743 => 'Music Videos|Blues|Electric Blues',
3892             1744 => "Music Videos|Children's Music|Lullabies",
3893             1745 => "Music Videos|Children's Music|Sing-Along",
3894             1746 => "Music Videos|Children's Music|Stories",
3895             1747 => 'Music Videos|Christian & Gospel|CCM',
3896             1748 => 'Music Videos|Christian & Gospel|Christian Metal',
3897             1749 => 'Music Videos|Christian & Gospel|Christian Pop',
3898             1750 => 'Music Videos|Christian & Gospel|Christian Rap',
3899             1751 => 'Music Videos|Christian & Gospel|Christian Rock',
3900             1752 => 'Music Videos|Christian & Gospel|Classic Christian',
3901             1753 => 'Music Videos|Christian & Gospel|Contemporary Gospel',
3902             1754 => 'Music Videos|Christian & Gospel|Gospel',
3903             1755 => 'Music Videos|Christian & Gospel|Praise & Worship',
3904             1756 => 'Music Videos|Christian & Gospel|Southern Gospel',
3905             1757 => 'Music Videos|Christian & Gospel|Traditional Gospel',
3906             1758 => 'Music Videos|Classical|Avant-Garde',
3907             1759 => 'Music Videos|Classical|Baroque Era',
3908             1760 => 'Music Videos|Classical|Chamber Music',
3909             1761 => 'Music Videos|Classical|Chant',
3910             1762 => 'Music Videos|Classical|Choral',
3911             1763 => 'Music Videos|Classical|Classical Crossover',
3912             1764 => 'Music Videos|Classical|Early Music',
3913             1765 => 'Music Videos|Classical|Impressionist',
3914             1766 => 'Music Videos|Classical|Medieval Era',
3915             1767 => 'Music Videos|Classical|Minimalism',
3916             1768 => 'Music Videos|Classical|Modern Era',
3917             1769 => 'Music Videos|Classical|Orchestral',
3918             1770 => 'Music Videos|Classical|Renaissance',
3919             1771 => 'Music Videos|Classical|Romantic Era',
3920             1772 => 'Music Videos|Classical|Wedding Music',
3921             1773 => 'Music Videos|Comedy|Novelty',
3922             1774 => 'Music Videos|Comedy|Standup Comedy',
3923             1775 => 'Music Videos|Country|Alternative Country',
3924             1776 => 'Music Videos|Country|Americana',
3925             1777 => 'Music Videos|Country|Bluegrass',
3926             1778 => 'Music Videos|Country|Contemporary Bluegrass',
3927             1779 => 'Music Videos|Country|Contemporary Country',
3928             1780 => 'Music Videos|Country|Country Gospel',
3929             1781 => 'Music Videos|Country|Honky Tonk',
3930             1782 => 'Music Videos|Country|Outlaw Country',
3931             1783 => 'Music Videos|Country|Traditional Bluegrass',
3932             1784 => 'Music Videos|Country|Traditional Country',
3933             1785 => 'Music Videos|Country|Urban Cowboy',
3934             1786 => 'Music Videos|Dance|Breakbeat',
3935             1787 => 'Music Videos|Dance|Exercise',
3936             1788 => 'Music Videos|Dance|Garage',
3937             1789 => 'Music Videos|Dance|Hardcore',
3938             1790 => 'Music Videos|Dance|House',
3939             1791 => "Music Videos|Dance|Jungle/Drum'n'bass",
3940             1792 => 'Music Videos|Dance|Techno',
3941             1793 => 'Music Videos|Dance|Trance',
3942             1794 => 'Music Videos|Easy Listening|Lounge',
3943             1795 => 'Music Videos|Easy Listening|Swing',
3944             1796 => 'Music Videos|Electronic|Ambient',
3945             1797 => 'Music Videos|Electronic|Downtempo',
3946             1798 => 'Music Videos|Electronic|Electronica',
3947             1799 => 'Music Videos|Electronic|IDM/Experimental',
3948             1800 => 'Music Videos|Electronic|Industrial',
3949             1801 => 'Music Videos|Hip-Hop/Rap|Alternative Rap',
3950             1802 => 'Music Videos|Hip-Hop/Rap|Dirty South',
3951             1803 => 'Music Videos|Hip-Hop/Rap|East Coast Rap',
3952             1804 => 'Music Videos|Hip-Hop/Rap|Gangsta Rap',
3953             1805 => 'Music Videos|Hip-Hop/Rap|Hardcore Rap',
3954             1806 => 'Music Videos|Hip-Hop/Rap|Hip-Hop',
3955             1807 => 'Music Videos|Hip-Hop/Rap|Latin Rap',
3956             1808 => 'Music Videos|Hip-Hop/Rap|Old School Rap',
3957             1809 => 'Music Videos|Hip-Hop/Rap|Rap',
3958             1810 => 'Music Videos|Hip-Hop/Rap|Underground Rap',
3959             1811 => 'Music Videos|Hip-Hop/Rap|West Coast Rap',
3960             1812 => 'Music Videos|Holiday|Chanukah',
3961             1813 => 'Music Videos|Holiday|Christmas',
3962             1814 => "Music Videos|Holiday|Christmas: Children's",
3963             1815 => 'Music Videos|Holiday|Christmas: Classic',
3964             1816 => 'Music Videos|Holiday|Christmas: Classical',
3965             1817 => 'Music Videos|Holiday|Christmas: Jazz',
3966             1818 => 'Music Videos|Holiday|Christmas: Modern',
3967             1819 => 'Music Videos|Holiday|Christmas: Pop',
3968             1820 => 'Music Videos|Holiday|Christmas: R&B',
3969             1821 => 'Music Videos|Holiday|Christmas: Religious',
3970             1822 => 'Music Videos|Holiday|Christmas: Rock',
3971             1823 => 'Music Videos|Holiday|Easter',
3972             1824 => 'Music Videos|Holiday|Halloween',
3973             1825 => 'Music Videos|Holiday|Thanksgiving',
3974             1826 => 'Music Videos|Jazz|Avant-Garde Jazz',
3975             1828 => 'Music Videos|Jazz|Bop',
3976             1829 => 'Music Videos|Jazz|Contemporary Jazz',
3977             1830 => 'Music Videos|Jazz|Cool Jazz',
3978             1831 => 'Music Videos|Jazz|Crossover Jazz',
3979             1832 => 'Music Videos|Jazz|Dixieland',
3980             1833 => 'Music Videos|Jazz|Fusion',
3981             1834 => 'Music Videos|Jazz|Hard Bop',
3982             1835 => 'Music Videos|Jazz|Latin Jazz',
3983             1836 => 'Music Videos|Jazz|Mainstream Jazz',
3984             1837 => 'Music Videos|Jazz|Ragtime',
3985             1838 => 'Music Videos|Jazz|Smooth Jazz',
3986             1839 => 'Music Videos|Jazz|Trad Jazz',
3987             1840 => 'Music Videos|Latin|Alternative & Rock in Spanish',
3988             1841 => 'Music Videos|Latin|Baladas y Boleros',
3989             1842 => 'Music Videos|Latin|Contemporary Latin',
3990             1843 => 'Music Videos|Latin|Latin Jazz',
3991             1844 => 'Music Videos|Latin|Latin Urban',
3992             1845 => 'Music Videos|Latin|Pop in Spanish',
3993             1846 => 'Music Videos|Latin|Raices',
3994             1847 => 'Music Videos|Latin|Musica Mexicana', # (Música Mexicana)
3995             1848 => 'Music Videos|Latin|Salsa y Tropical',
3996             1849 => 'Music Videos|New Age|Healing',
3997             1850 => 'Music Videos|New Age|Meditation',
3998             1851 => 'Music Videos|New Age|Nature',
3999             1852 => 'Music Videos|New Age|Relaxation',
4000             1853 => 'Music Videos|New Age|Travel',
4001             1854 => 'Music Videos|Pop|Adult Contemporary',
4002             1855 => 'Music Videos|Pop|Britpop',
4003             1856 => 'Music Videos|Pop|Pop/Rock',
4004             1857 => 'Music Videos|Pop|Soft Rock',
4005             1858 => 'Music Videos|Pop|Teen Pop',
4006             1859 => 'Music Videos|R&B/Soul|Contemporary R&B',
4007             1860 => 'Music Videos|R&B/Soul|Disco',
4008             1861 => 'Music Videos|R&B/Soul|Doo Wop',
4009             1862 => 'Music Videos|R&B/Soul|Funk',
4010             1863 => 'Music Videos|R&B/Soul|Motown',
4011             1864 => 'Music Videos|R&B/Soul|Neo-Soul',
4012             1865 => 'Music Videos|R&B/Soul|Soul',
4013             1866 => 'Music Videos|Reggae|Modern Dancehall',
4014             1867 => 'Music Videos|Reggae|Dub',
4015             1868 => 'Music Videos|Reggae|Roots Reggae',
4016             1869 => 'Music Videos|Reggae|Ska',
4017             1870 => 'Music Videos|Rock|Adult Alternative',
4018             1871 => 'Music Videos|Rock|American Trad Rock',
4019             1872 => 'Music Videos|Rock|Arena Rock',
4020             1873 => 'Music Videos|Rock|Blues-Rock',
4021             1874 => 'Music Videos|Rock|British Invasion',
4022             1875 => 'Music Videos|Rock|Death Metal/Black Metal',
4023             1876 => 'Music Videos|Rock|Glam Rock',
4024             1877 => 'Music Videos|Rock|Hair Metal',
4025             1878 => 'Music Videos|Rock|Hard Rock',
4026             1879 => 'Music Videos|Rock|Jam Bands',
4027             1880 => 'Music Videos|Rock|Prog-Rock/Art Rock',
4028             1881 => 'Music Videos|Rock|Psychedelic',
4029             1882 => 'Music Videos|Rock|Rock & Roll',
4030             1883 => 'Music Videos|Rock|Rockabilly',
4031             1884 => 'Music Videos|Rock|Roots Rock',
4032             1885 => 'Music Videos|Rock|Singer/Songwriter',
4033             1886 => 'Music Videos|Rock|Southern Rock',
4034             1887 => 'Music Videos|Rock|Surf',
4035             1888 => 'Music Videos|Rock|Tex-Mex',
4036             1889 => 'Music Videos|Singer/Songwriter|Alternative Folk',
4037             1890 => 'Music Videos|Singer/Songwriter|Contemporary Folk',
4038             1891 => 'Music Videos|Singer/Songwriter|Contemporary Singer/Songwriter',
4039             1892 => 'Music Videos|Singer/Songwriter|Folk-Rock',
4040             1893 => 'Music Videos|Singer/Songwriter|New Acoustic',
4041             1894 => 'Music Videos|Singer/Songwriter|Traditional Folk',
4042             1895 => 'Music Videos|Soundtrack|Foreign Cinema',
4043             1896 => 'Music Videos|Soundtrack|Musicals',
4044             1897 => 'Music Videos|Soundtrack|Original Score',
4045             1898 => 'Music Videos|Soundtrack|Soundtrack',
4046             1899 => 'Music Videos|Soundtrack|TV Soundtrack',
4047             1900 => 'Music Videos|Vocal|Standards',
4048             1901 => 'Music Videos|Vocal|Traditional Pop',
4049             1902 => 'Music Videos|Jazz|Vocal Jazz',
4050             1903 => 'Music Videos|Vocal|Vocal Pop',
4051             1904 => 'Music Videos|African',
4052             1905 => 'Music Videos|African|Afro-Beat',
4053             1906 => 'Music Videos|African|Afro-Pop',
4054             1907 => 'Music Videos|World|Asia',
4055             1908 => 'Music Videos|World|Australia',
4056             1909 => 'Music Videos|World|Cajun',
4057             1910 => 'Music Videos|World|Caribbean',
4058             1911 => 'Music Videos|World|Celtic',
4059             1912 => 'Music Videos|World|Celtic Folk',
4060             1913 => 'Music Videos|World|Contemporary Celtic',
4061             1914 => 'Music Videos|World|Europe',
4062             1915 => 'Music Videos|World|France',
4063             1916 => 'Music Videos|World|Hawaii',
4064             1917 => 'Music Videos|World|Japan',
4065             1918 => 'Music Videos|World|Klezmer',
4066             1919 => 'Music Videos|World|North America',
4067             1920 => 'Music Videos|World|Polka',
4068             1921 => 'Music Videos|World|South Africa',
4069             1922 => 'Music Videos|World|South America',
4070             1923 => 'Music Videos|World|Traditional Celtic',
4071             1924 => 'Music Videos|World|Worldbeat',
4072             1925 => 'Music Videos|World|Zydeco',
4073             1926 => 'Music Videos|Christian & Gospel',
4074             1928 => 'Music Videos|Classical|Art Song',
4075             1929 => 'Music Videos|Classical|Brass & Woodwinds',
4076             1930 => 'Music Videos|Classical|Solo Instrumental',
4077             1931 => 'Music Videos|Classical|Contemporary Era',
4078             1932 => 'Music Videos|Classical|Oratorio',
4079             1933 => 'Music Videos|Classical|Cantata',
4080             1934 => 'Music Videos|Classical|Electronic',
4081             1935 => 'Music Videos|Classical|Sacred',
4082             1936 => 'Music Videos|Classical|Guitar',
4083             1938 => 'Music Videos|Classical|Violin',
4084             1939 => 'Music Videos|Classical|Cello',
4085             1940 => 'Music Videos|Classical|Percussion',
4086             1941 => 'Music Videos|Electronic|Dubstep',
4087             1942 => 'Music Videos|Electronic|Bass',
4088             1943 => 'Music Videos|Hip-Hop/Rap|UK Hip-Hop',
4089             1944 => 'Music Videos|Reggae|Lovers Rock',
4090             1945 => 'Music Videos|Alternative|EMO',
4091             1946 => 'Music Videos|Alternative|Pop Punk',
4092             1947 => 'Music Videos|Alternative|Indie Pop',
4093             1948 => 'Music Videos|New Age|Yoga',
4094             1949 => 'Music Videos|Pop|Tribute',
4095             1950 => 'Music Videos|Pop|Shows',
4096             1951 => 'Music Videos|Cuban',
4097             1952 => 'Music Videos|Cuban|Mambo',
4098             1953 => 'Music Videos|Cuban|Chachacha',
4099             1954 => 'Music Videos|Cuban|Guajira',
4100             1955 => 'Music Videos|Cuban|Son',
4101             1956 => 'Music Videos|Cuban|Bolero',
4102             1957 => 'Music Videos|Cuban|Guaracha',
4103             1958 => 'Music Videos|Cuban|Timba',
4104             1959 => 'Music Videos|Soundtrack|Video Game',
4105             1960 => 'Music Videos|Indian|Regional Indian|Punjabi|Punjabi Pop',
4106             1961 => 'Music Videos|Indian|Regional Indian|Bengali|Rabindra Sangeet',
4107             1962 => 'Music Videos|Indian|Regional Indian|Malayalam',
4108             1963 => 'Music Videos|Indian|Regional Indian|Kannada',
4109             1964 => 'Music Videos|Indian|Regional Indian|Marathi',
4110             1965 => 'Music Videos|Indian|Regional Indian|Gujarati',
4111             1966 => 'Music Videos|Indian|Regional Indian|Assamese',
4112             1967 => 'Music Videos|Indian|Regional Indian|Bhojpuri',
4113             1968 => 'Music Videos|Indian|Regional Indian|Haryanvi',
4114             1969 => 'Music Videos|Indian|Regional Indian|Odia',
4115             1970 => 'Music Videos|Indian|Regional Indian|Rajasthani',
4116             1971 => 'Music Videos|Indian|Regional Indian|Urdu',
4117             1972 => 'Music Videos|Indian|Regional Indian|Punjabi',
4118             1973 => 'Music Videos|Indian|Regional Indian|Bengali',
4119             1974 => 'Music Videos|Indian|Indian Classical|Carnatic Classical',
4120             1975 => 'Music Videos|Indian|Indian Classical|Hindustani Classical',
4121             1976 => 'Music Videos|African|Afro House',
4122             1977 => 'Music Videos|African|Afro Soul',
4123             1978 => 'Music Videos|African|Afrobeats',
4124             1979 => 'Music Videos|African|Benga',
4125             1980 => 'Music Videos|African|Bongo-Flava',
4126             1981 => 'Music Videos|African|Coupe-Decale',
4127             1982 => 'Music Videos|African|Gqom',
4128             1983 => 'Music Videos|African|Highlife',
4129             1984 => 'Music Videos|African|Kuduro',
4130             1985 => 'Music Videos|African|Kizomba',
4131             1986 => 'Music Videos|African|Kwaito',
4132             1987 => 'Music Videos|African|Mbalax',
4133             1988 => 'Music Videos|African|Ndombolo',
4134             1989 => 'Music Videos|African|Shangaan Electro',
4135             1990 => 'Music Videos|African|Soukous',
4136             1991 => 'Music Videos|African|Taarab',
4137             1992 => 'Music Videos|African|Zouglou',
4138             1993 => 'Music Videos|Turkish|Ozgun',
4139             1994 => 'Music Videos|Turkish|Fantezi',
4140             1995 => 'Music Videos|Turkish|Religious',
4141             1996 => 'Music Videos|Pop|Turkish Pop',
4142             1997 => 'Music Videos|Rock|Turkish Rock',
4143             1998 => 'Music Videos|Alternative|Turkish Alternative',
4144             1999 => 'Music Videos|Hip-Hop/Rap|Turkish Hip-Hop/Rap',
4145             2000 => 'Music Videos|African|Maskandi',
4146             2001 => 'Music Videos|Russian|Russian Romance',
4147             2002 => 'Music Videos|Russian|Russian Bard',
4148             2003 => 'Music Videos|Russian|Russian Pop',
4149             2004 => 'Music Videos|Russian|Russian Rock',
4150             2005 => 'Music Videos|Russian|Russian Hip-Hop',
4151             2006 => 'Music Videos|Arabic|Levant',
4152             2007 => 'Music Videos|Arabic|Levant|Dabke',
4153             2008 => 'Music Videos|Arabic|Maghreb Rai',
4154             2009 => 'Music Videos|Arabic|Khaleeji|Khaleeji Jalsat',
4155             2010 => 'Music Videos|Arabic|Khaleeji|Khaleeji Shailat',
4156             2011 => 'Music Videos|Tarab',
4157             2012 => 'Music Videos|Tarab|Iraqi Tarab',
4158             2013 => 'Music Videos|Tarab|Egyptian Tarab',
4159             2014 => 'Music Videos|Tarab|Khaleeji Tarab',
4160             2015 => 'Music Videos|Pop|Levant Pop',
4161             2016 => 'Music Videos|Pop|Iraqi Pop',
4162             2017 => 'Music Videos|Pop|Egyptian Pop',
4163             2018 => 'Music Videos|Pop|Maghreb Pop',
4164             2019 => 'Music Videos|Pop|Khaleeji Pop',
4165             2020 => 'Music Videos|Hip-Hop/Rap|Levant Hip-Hop',
4166             2021 => 'Music Videos|Hip-Hop/Rap|Egyptian Hip-Hop',
4167             2022 => 'Music Videos|Hip-Hop/Rap|Maghreb Hip-Hop',
4168             2023 => 'Music Videos|Hip-Hop/Rap|Khaleeji Hip-Hop',
4169             2024 => 'Music Videos|Alternative|Indie Levant',
4170             2025 => 'Music Videos|Alternative|Indie Egyptian',
4171             2026 => 'Music Videos|Alternative|Indie Maghreb',
4172             2027 => 'Music Videos|Electronic|Levant Electronic',
4173             2028 => "Music Videos|Electronic|Electro-Cha'abi",
4174             2029 => 'Music Videos|Electronic|Maghreb Electronic',
4175             2030 => 'Music Videos|Folk|Iraqi Folk',
4176             2031 => 'Music Videos|Folk|Khaleeji Folk',
4177             2032 => 'Music Videos|Dance|Maghreb Dance',
4178             4000 => 'TV Shows|Comedy',
4179             4001 => 'TV Shows|Drama',
4180             4002 => 'TV Shows|Animation',
4181             4003 => 'TV Shows|Action & Adventure',
4182             4004 => 'TV Shows|Classics',
4183             4005 => 'TV Shows|Kids & Family',
4184             4006 => 'TV Shows|Nonfiction',
4185             4007 => 'TV Shows|Reality TV',
4186             4008 => 'TV Shows|Sci-Fi & Fantasy',
4187             4009 => 'TV Shows|Sports',
4188             4010 => 'TV Shows|Teens',
4189             4011 => 'TV Shows|Latino TV',
4190             4401 => 'Movies|Action & Adventure',
4191             4402 => 'Movies|Anime',
4192             4403 => 'Movies|Classics',
4193             4404 => 'Movies|Comedy',
4194             4405 => 'Movies|Documentary',
4195             4406 => 'Movies|Drama',
4196             4407 => 'Movies|Foreign',
4197             4408 => 'Movies|Horror',
4198             4409 => 'Movies|Independent',
4199             4410 => 'Movies|Kids & Family',
4200             4411 => 'Movies|Musicals',
4201             4412 => 'Movies|Romance',
4202             4413 => 'Movies|Sci-Fi & Fantasy',
4203             4414 => 'Movies|Short Films',
4204             4415 => 'Movies|Special Interest',
4205             4416 => 'Movies|Thriller',
4206             4417 => 'Movies|Sports',
4207             4418 => 'Movies|Western',
4208             4419 => 'Movies|Urban',
4209             4420 => 'Movies|Holiday',
4210             4421 => 'Movies|Made for TV',
4211             4422 => 'Movies|Concert Films',
4212             4423 => 'Movies|Music Documentaries',
4213             4424 => 'Movies|Music Feature Films',
4214             4425 => 'Movies|Japanese Cinema',
4215             4426 => 'Movies|Jidaigeki',
4216             4427 => 'Movies|Tokusatsu',
4217             4428 => 'Movies|Korean Cinema',
4218             4429 => 'Movies|Russian',
4219             4430 => 'Movies|Turkish',
4220             4431 => 'Movies|Bollywood',
4221             4432 => 'Movies|Regional Indian',
4222             4433 => 'Movies|Middle Eastern',
4223             4434 => 'Movies|African',
4224             6000 => 'App Store|Business',
4225             6001 => 'App Store|Weather',
4226             6002 => 'App Store|Utilities',
4227             6003 => 'App Store|Travel',
4228             6004 => 'App Store|Sports',
4229             6005 => 'App Store|Social Networking',
4230             6006 => 'App Store|Reference',
4231             6007 => 'App Store|Productivity',
4232             6008 => 'App Store|Photo & Video',
4233             6009 => 'App Store|News',
4234             6010 => 'App Store|Navigation',
4235             6011 => 'App Store|Music',
4236             6012 => 'App Store|Lifestyle',
4237             6013 => 'App Store|Health & Fitness',
4238             6014 => 'App Store|Games',
4239             6015 => 'App Store|Finance',
4240             6016 => 'App Store|Entertainment',
4241             6017 => 'App Store|Education',
4242             6018 => 'App Store|Books',
4243             6020 => 'App Store|Medical',
4244             6021 => 'App Store|Magazines & Newspapers',
4245             6022 => 'App Store|Catalogs',
4246             6023 => 'App Store|Food & Drink',
4247             6024 => 'App Store|Shopping',
4248             6025 => 'App Store|Stickers',
4249             6026 => 'App Store|Developer Tools',
4250             6027 => 'App Store|Graphics & Design',
4251             7001 => 'App Store|Games|Action',
4252             7002 => 'App Store|Games|Adventure',
4253             7003 => 'App Store|Games|Casual',
4254             7004 => 'App Store|Games|Board',
4255             7005 => 'App Store|Games|Card',
4256             7006 => 'App Store|Games|Casino',
4257             7007 => 'App Store|Games|Dice',
4258             7008 => 'App Store|Games|Educational',
4259             7009 => 'App Store|Games|Family',
4260             7011 => 'App Store|Games|Music',
4261             7012 => 'App Store|Games|Puzzle',
4262             7013 => 'App Store|Games|Racing',
4263             7014 => 'App Store|Games|Role Playing',
4264             7015 => 'App Store|Games|Simulation',
4265             7016 => 'App Store|Games|Sports',
4266             7017 => 'App Store|Games|Strategy',
4267             7018 => 'App Store|Games|Trivia',
4268             7019 => 'App Store|Games|Word',
4269             8001 => 'Tones|Ringtones|Alternative',
4270             8002 => 'Tones|Ringtones|Blues',
4271             8003 => "Tones|Ringtones|Children's Music",
4272             8004 => 'Tones|Ringtones|Classical',
4273             8005 => 'Tones|Ringtones|Comedy',
4274             8006 => 'Tones|Ringtones|Country',
4275             8007 => 'Tones|Ringtones|Dance',
4276             8008 => 'Tones|Ringtones|Electronic',
4277             8009 => 'Tones|Ringtones|Enka',
4278             8010 => 'Tones|Ringtones|French Pop',
4279             8011 => 'Tones|Ringtones|German Folk',
4280             8012 => 'Tones|Ringtones|German Pop',
4281             8013 => 'Tones|Ringtones|Hip-Hop/Rap',
4282             8014 => 'Tones|Ringtones|Holiday',
4283             8015 => 'Tones|Ringtones|Inspirational',
4284             8016 => 'Tones|Ringtones|J-Pop',
4285             8017 => 'Tones|Ringtones|Jazz',
4286             8018 => 'Tones|Ringtones|Kayokyoku',
4287             8019 => 'Tones|Ringtones|Latin',
4288             8020 => 'Tones|Ringtones|New Age',
4289             8021 => 'Tones|Ringtones|Classical|Opera',
4290             8022 => 'Tones|Ringtones|Pop',
4291             8023 => 'Tones|Ringtones|R&B/Soul',
4292             8024 => 'Tones|Ringtones|Reggae',
4293             8025 => 'Tones|Ringtones|Rock',
4294             8026 => 'Tones|Ringtones|Singer/Songwriter',
4295             8027 => 'Tones|Ringtones|Soundtrack',
4296             8028 => 'Tones|Ringtones|Spoken Word',
4297             8029 => 'Tones|Ringtones|Vocal',
4298             8030 => 'Tones|Ringtones|World',
4299             8050 => 'Tones|Alert Tones|Sound Effects',
4300             8051 => 'Tones|Alert Tones|Dialogue',
4301             8052 => 'Tones|Alert Tones|Music',
4302             8053 => 'Tones|Ringtones',
4303             8054 => 'Tones|Alert Tones',
4304             8055 => 'Tones|Ringtones|Alternative|Chinese Alt',
4305             8056 => 'Tones|Ringtones|Alternative|College Rock',
4306             8057 => 'Tones|Ringtones|Alternative|Goth Rock',
4307             8058 => 'Tones|Ringtones|Alternative|Grunge',
4308             8059 => 'Tones|Ringtones|Alternative|Indie Rock',
4309             8060 => 'Tones|Ringtones|Alternative|Korean Indie',
4310             8061 => 'Tones|Ringtones|Alternative|New Wave',
4311             8062 => 'Tones|Ringtones|Alternative|Punk',
4312             8063 => 'Tones|Ringtones|Anime',
4313             8064 => 'Tones|Ringtones|Arabic',
4314             8065 => 'Tones|Ringtones|Arabic|Arabic Pop',
4315             8066 => 'Tones|Ringtones|Arabic|Islamic',
4316             8067 => 'Tones|Ringtones|Arabic|Khaleeji',
4317             8068 => 'Tones|Ringtones|Arabic|North African',
4318             8069 => 'Tones|Ringtones|Blues|Acoustic Blues',
4319             8070 => 'Tones|Ringtones|Blues|Chicago Blues',
4320             8071 => 'Tones|Ringtones|Blues|Classic Blues',
4321             8072 => 'Tones|Ringtones|Blues|Contemporary Blues',
4322             8073 => 'Tones|Ringtones|Blues|Country Blues',
4323             8074 => 'Tones|Ringtones|Blues|Delta Blues',
4324             8075 => 'Tones|Ringtones|Blues|Electric Blues',
4325             8076 => 'Tones|Ringtones|Brazilian',
4326             8077 => 'Tones|Ringtones|Brazilian|Axe', # (Axé)
4327             8078 => 'Tones|Ringtones|Brazilian|Baile Funk',
4328             8079 => 'Tones|Ringtones|Brazilian|Bossa Nova',
4329             8080 => 'Tones|Ringtones|Brazilian|Choro',
4330             8081 => 'Tones|Ringtones|Brazilian|Forro', # (Forró)
4331             8082 => 'Tones|Ringtones|Brazilian|Frevo',
4332             8083 => 'Tones|Ringtones|Brazilian|MPB',
4333             8084 => 'Tones|Ringtones|Brazilian|Pagode',
4334             8085 => 'Tones|Ringtones|Brazilian|Samba',
4335             8086 => 'Tones|Ringtones|Brazilian|Sertanejo',
4336             8087 => "Tones|Ringtones|Children's Music|Lullabies",
4337             8088 => "Tones|Ringtones|Children's Music|Sing-Along",
4338             8089 => "Tones|Ringtones|Children's Music|Stories",
4339             8090 => 'Tones|Ringtones|Chinese',
4340             8091 => 'Tones|Ringtones|Chinese|Chinese Classical',
4341             8092 => 'Tones|Ringtones|Chinese|Chinese Flute',
4342             8093 => 'Tones|Ringtones|Chinese|Chinese Opera',
4343             8094 => 'Tones|Ringtones|Chinese|Chinese Orchestral',
4344             8095 => 'Tones|Ringtones|Chinese|Chinese Regional Folk',
4345             8096 => 'Tones|Ringtones|Chinese|Chinese Strings',
4346             8097 => 'Tones|Ringtones|Chinese|Taiwanese Folk',
4347             8098 => 'Tones|Ringtones|Chinese|Tibetan Native Music',
4348             8099 => 'Tones|Ringtones|Christian & Gospel',
4349             8100 => 'Tones|Ringtones|Christian & Gospel|CCM',
4350             8101 => 'Tones|Ringtones|Christian & Gospel|Christian Metal',
4351             8102 => 'Tones|Ringtones|Christian & Gospel|Christian Pop',
4352             8103 => 'Tones|Ringtones|Christian & Gospel|Christian Rap',
4353             8104 => 'Tones|Ringtones|Christian & Gospel|Christian Rock',
4354             8105 => 'Tones|Ringtones|Christian & Gospel|Classic Christian',
4355             8106 => 'Tones|Ringtones|Christian & Gospel|Contemporary Gospel',
4356             8107 => 'Tones|Ringtones|Christian & Gospel|Gospel',
4357             8108 => 'Tones|Ringtones|Christian & Gospel|Praise & Worship',
4358             8109 => 'Tones|Ringtones|Christian & Gospel|Southern Gospel',
4359             8110 => 'Tones|Ringtones|Christian & Gospel|Traditional Gospel',
4360             8111 => 'Tones|Ringtones|Classical|Avant-Garde',
4361             8112 => 'Tones|Ringtones|Classical|Baroque Era',
4362             8113 => 'Tones|Ringtones|Classical|Chamber Music',
4363             8114 => 'Tones|Ringtones|Classical|Chant',
4364             8115 => 'Tones|Ringtones|Classical|Choral',
4365             8116 => 'Tones|Ringtones|Classical|Classical Crossover',
4366             8117 => 'Tones|Ringtones|Classical|Early Music',
4367             8118 => 'Tones|Ringtones|Classical|High Classical',
4368             8119 => 'Tones|Ringtones|Classical|Impressionist',
4369             8120 => 'Tones|Ringtones|Classical|Medieval Era',
4370             8121 => 'Tones|Ringtones|Classical|Minimalism',
4371             8122 => 'Tones|Ringtones|Classical|Modern Era',
4372             8123 => 'Tones|Ringtones|Classical|Orchestral',
4373             8124 => 'Tones|Ringtones|Classical|Renaissance',
4374             8125 => 'Tones|Ringtones|Classical|Romantic Era',
4375             8126 => 'Tones|Ringtones|Classical|Wedding Music',
4376             8127 => 'Tones|Ringtones|Comedy|Novelty',
4377             8128 => 'Tones|Ringtones|Comedy|Standup Comedy',
4378             8129 => 'Tones|Ringtones|Country|Alternative Country',
4379             8130 => 'Tones|Ringtones|Country|Americana',
4380             8131 => 'Tones|Ringtones|Country|Bluegrass',
4381             8132 => 'Tones|Ringtones|Country|Contemporary Bluegrass',
4382             8133 => 'Tones|Ringtones|Country|Contemporary Country',
4383             8134 => 'Tones|Ringtones|Country|Country Gospel',
4384             8135 => 'Tones|Ringtones|Country|Honky Tonk',
4385             8136 => 'Tones|Ringtones|Country|Outlaw Country',
4386             8137 => 'Tones|Ringtones|Country|Thai Country',
4387             8138 => 'Tones|Ringtones|Country|Traditional Bluegrass',
4388             8139 => 'Tones|Ringtones|Country|Traditional Country',
4389             8140 => 'Tones|Ringtones|Country|Urban Cowboy',
4390             8141 => 'Tones|Ringtones|Dance|Breakbeat',
4391             8142 => 'Tones|Ringtones|Dance|Exercise',
4392             8143 => 'Tones|Ringtones|Dance|Garage',
4393             8144 => 'Tones|Ringtones|Dance|Hardcore',
4394             8145 => 'Tones|Ringtones|Dance|House',
4395             8146 => "Tones|Ringtones|Dance|Jungle/Drum'n'bass",
4396             8147 => 'Tones|Ringtones|Dance|Techno',
4397             8148 => 'Tones|Ringtones|Dance|Trance',
4398             8149 => 'Tones|Ringtones|Disney',
4399             8150 => 'Tones|Ringtones|Easy Listening',
4400             8151 => 'Tones|Ringtones|Easy Listening|Lounge',
4401             8152 => 'Tones|Ringtones|Easy Listening|Swing',
4402             8153 => 'Tones|Ringtones|Electronic|Ambient',
4403             8154 => 'Tones|Ringtones|Electronic|Downtempo',
4404             8155 => 'Tones|Ringtones|Electronic|Electronica',
4405             8156 => 'Tones|Ringtones|Electronic|IDM/Experimental',
4406             8157 => 'Tones|Ringtones|Electronic|Industrial',
4407             8158 => 'Tones|Ringtones|Fitness & Workout',
4408             8159 => 'Tones|Ringtones|Folk',
4409             8160 => 'Tones|Ringtones|Hip-Hop/Rap|Alternative Rap',
4410             8161 => 'Tones|Ringtones|Hip-Hop/Rap|Chinese Hip-Hop',
4411             8162 => 'Tones|Ringtones|Hip-Hop/Rap|Dirty South',
4412             8163 => 'Tones|Ringtones|Hip-Hop/Rap|East Coast Rap',
4413             8164 => 'Tones|Ringtones|Hip-Hop/Rap|Gangsta Rap',
4414             8165 => 'Tones|Ringtones|Hip-Hop/Rap|Hardcore Rap',
4415             8166 => 'Tones|Ringtones|Hip-Hop/Rap|Hip-Hop',
4416             8167 => 'Tones|Ringtones|Hip-Hop/Rap|Korean Hip-Hop',
4417             8168 => 'Tones|Ringtones|Hip-Hop/Rap|Latin Rap',
4418             8169 => 'Tones|Ringtones|Hip-Hop/Rap|Old School Rap',
4419             8170 => 'Tones|Ringtones|Hip-Hop/Rap|Rap',
4420             8171 => 'Tones|Ringtones|Hip-Hop/Rap|Underground Rap',
4421             8172 => 'Tones|Ringtones|Hip-Hop/Rap|West Coast Rap',
4422             8173 => 'Tones|Ringtones|Holiday|Chanukah',
4423             8174 => 'Tones|Ringtones|Holiday|Christmas',
4424             8175 => "Tones|Ringtones|Holiday|Christmas: Children's",
4425             8176 => 'Tones|Ringtones|Holiday|Christmas: Classic',
4426             8177 => 'Tones|Ringtones|Holiday|Christmas: Classical',
4427             8178 => 'Tones|Ringtones|Holiday|Christmas: Jazz',
4428             8179 => 'Tones|Ringtones|Holiday|Christmas: Modern',
4429             8180 => 'Tones|Ringtones|Holiday|Christmas: Pop',
4430             8181 => 'Tones|Ringtones|Holiday|Christmas: R&B',
4431             8182 => 'Tones|Ringtones|Holiday|Christmas: Religious',
4432             8183 => 'Tones|Ringtones|Holiday|Christmas: Rock',
4433             8184 => 'Tones|Ringtones|Holiday|Easter',
4434             8185 => 'Tones|Ringtones|Holiday|Halloween',
4435             8186 => 'Tones|Ringtones|Holiday|Thanksgiving',
4436             8187 => 'Tones|Ringtones|Indian',
4437             8188 => 'Tones|Ringtones|Indian|Bollywood',
4438             8189 => 'Tones|Ringtones|Indian|Devotional & Spiritual',
4439             8190 => 'Tones|Ringtones|Indian|Ghazals',
4440             8191 => 'Tones|Ringtones|Indian|Indian Classical',
4441             8192 => 'Tones|Ringtones|Indian|Indian Folk',
4442             8193 => 'Tones|Ringtones|Indian|Indian Pop',
4443             8194 => 'Tones|Ringtones|Indian|Regional Indian',
4444             8195 => 'Tones|Ringtones|Indian|Sufi',
4445             8196 => 'Tones|Ringtones|Indian|Regional Indian|Tamil',
4446             8197 => 'Tones|Ringtones|Indian|Regional Indian|Telugu',
4447             8198 => 'Tones|Ringtones|Instrumental',
4448             8199 => 'Tones|Ringtones|Jazz|Avant-Garde Jazz',
4449             8201 => 'Tones|Ringtones|Jazz|Big Band',
4450             8202 => 'Tones|Ringtones|Jazz|Bop',
4451             8203 => 'Tones|Ringtones|Jazz|Contemporary Jazz',
4452             8204 => 'Tones|Ringtones|Jazz|Cool Jazz',
4453             8205 => 'Tones|Ringtones|Jazz|Crossover Jazz',
4454             8206 => 'Tones|Ringtones|Jazz|Dixieland',
4455             8207 => 'Tones|Ringtones|Jazz|Fusion',
4456             8208 => 'Tones|Ringtones|Jazz|Hard Bop',
4457             8209 => 'Tones|Ringtones|Jazz|Latin Jazz',
4458             8210 => 'Tones|Ringtones|Jazz|Mainstream Jazz',
4459             8211 => 'Tones|Ringtones|Jazz|Ragtime',
4460             8212 => 'Tones|Ringtones|Jazz|Smooth Jazz',
4461             8213 => 'Tones|Ringtones|Jazz|Trad Jazz',
4462             8214 => 'Tones|Ringtones|Pop|K-Pop',
4463             8215 => 'Tones|Ringtones|Karaoke',
4464             8216 => 'Tones|Ringtones|Korean',
4465             8217 => 'Tones|Ringtones|Korean|Korean Classical',
4466             8218 => 'Tones|Ringtones|Korean|Korean Trad Instrumental',
4467             8219 => 'Tones|Ringtones|Korean|Korean Trad Song',
4468             8220 => 'Tones|Ringtones|Korean|Korean Trad Theater',
4469             8221 => 'Tones|Ringtones|Latin|Alternative & Rock in Spanish',
4470             8222 => 'Tones|Ringtones|Latin|Baladas y Boleros',
4471             8223 => 'Tones|Ringtones|Latin|Contemporary Latin',
4472             8224 => 'Tones|Ringtones|Latin|Latin Jazz',
4473             8225 => 'Tones|Ringtones|Latin|Latin Urban',
4474             8226 => 'Tones|Ringtones|Latin|Pop in Spanish',
4475             8227 => 'Tones|Ringtones|Latin|Raices',
4476             8228 => 'Tones|Ringtones|Latin|Musica Mexicana', # (Música Mexicana)
4477             8229 => 'Tones|Ringtones|Latin|Salsa y Tropical',
4478             8230 => 'Tones|Ringtones|Marching Bands',
4479             8231 => 'Tones|Ringtones|New Age|Healing',
4480             8232 => 'Tones|Ringtones|New Age|Meditation',
4481             8233 => 'Tones|Ringtones|New Age|Nature',
4482             8234 => 'Tones|Ringtones|New Age|Relaxation',
4483             8235 => 'Tones|Ringtones|New Age|Travel',
4484             8236 => 'Tones|Ringtones|Orchestral',
4485             8237 => 'Tones|Ringtones|Pop|Adult Contemporary',
4486             8238 => 'Tones|Ringtones|Pop|Britpop',
4487             8239 => 'Tones|Ringtones|Pop|C-Pop',
4488             8240 => 'Tones|Ringtones|Pop|Cantopop/HK-Pop',
4489             8241 => 'Tones|Ringtones|Pop|Indo Pop',
4490             8242 => 'Tones|Ringtones|Pop|Korean Folk-Pop',
4491             8243 => 'Tones|Ringtones|Pop|Malaysian Pop',
4492             8244 => 'Tones|Ringtones|Pop|Mandopop',
4493             8245 => 'Tones|Ringtones|Pop|Manilla Sound',
4494             8246 => 'Tones|Ringtones|Pop|Oldies',
4495             8247 => 'Tones|Ringtones|Pop|Original Pilipino Music',
4496             8248 => 'Tones|Ringtones|Pop|Pinoy Pop',
4497             8249 => 'Tones|Ringtones|Pop|Pop/Rock',
4498             8250 => 'Tones|Ringtones|Pop|Soft Rock',
4499             8251 => 'Tones|Ringtones|Pop|Tai-Pop',
4500             8252 => 'Tones|Ringtones|Pop|Teen Pop',
4501             8253 => 'Tones|Ringtones|Pop|Thai Pop',
4502             8254 => 'Tones|Ringtones|R&B/Soul|Contemporary R&B',
4503             8255 => 'Tones|Ringtones|R&B/Soul|Disco',
4504             8256 => 'Tones|Ringtones|R&B/Soul|Doo Wop',
4505             8257 => 'Tones|Ringtones|R&B/Soul|Funk',
4506             8258 => 'Tones|Ringtones|R&B/Soul|Motown',
4507             8259 => 'Tones|Ringtones|R&B/Soul|Neo-Soul',
4508             8260 => 'Tones|Ringtones|R&B/Soul|Soul',
4509             8261 => 'Tones|Ringtones|Reggae|Modern Dancehall',
4510             8262 => 'Tones|Ringtones|Reggae|Dub',
4511             8263 => 'Tones|Ringtones|Reggae|Roots Reggae',
4512             8264 => 'Tones|Ringtones|Reggae|Ska',
4513             8265 => 'Tones|Ringtones|Rock|Adult Alternative',
4514             8266 => 'Tones|Ringtones|Rock|American Trad Rock',
4515             8267 => 'Tones|Ringtones|Rock|Arena Rock',
4516             8268 => 'Tones|Ringtones|Rock|Blues-Rock',
4517             8269 => 'Tones|Ringtones|Rock|British Invasion',
4518             8270 => 'Tones|Ringtones|Rock|Chinese Rock',
4519             8271 => 'Tones|Ringtones|Rock|Death Metal/Black Metal',
4520             8272 => 'Tones|Ringtones|Rock|Glam Rock',
4521             8273 => 'Tones|Ringtones|Rock|Hair Metal',
4522             8274 => 'Tones|Ringtones|Rock|Hard Rock',
4523             8275 => 'Tones|Ringtones|Rock|Metal',
4524             8276 => 'Tones|Ringtones|Rock|Jam Bands',
4525             8277 => 'Tones|Ringtones|Rock|Korean Rock',
4526             8278 => 'Tones|Ringtones|Rock|Prog-Rock/Art Rock',
4527             8279 => 'Tones|Ringtones|Rock|Psychedelic',
4528             8280 => 'Tones|Ringtones|Rock|Rock & Roll',
4529             8281 => 'Tones|Ringtones|Rock|Rockabilly',
4530             8282 => 'Tones|Ringtones|Rock|Roots Rock',
4531             8283 => 'Tones|Ringtones|Rock|Singer/Songwriter',
4532             8284 => 'Tones|Ringtones|Rock|Southern Rock',
4533             8285 => 'Tones|Ringtones|Rock|Surf',
4534             8286 => 'Tones|Ringtones|Rock|Tex-Mex',
4535             8287 => 'Tones|Ringtones|Singer/Songwriter|Alternative Folk',
4536             8288 => 'Tones|Ringtones|Singer/Songwriter|Contemporary Folk',
4537             8289 => 'Tones|Ringtones|Singer/Songwriter|Contemporary Singer/Songwriter',
4538             8290 => 'Tones|Ringtones|Singer/Songwriter|Folk-Rock',
4539             8291 => 'Tones|Ringtones|Singer/Songwriter|New Acoustic',
4540             8292 => 'Tones|Ringtones|Singer/Songwriter|Traditional Folk',
4541             8293 => 'Tones|Ringtones|Soundtrack|Foreign Cinema',
4542             8294 => 'Tones|Ringtones|Soundtrack|Musicals',
4543             8295 => 'Tones|Ringtones|Soundtrack|Original Score',
4544             8296 => 'Tones|Ringtones|Soundtrack|Sound Effects',
4545             8297 => 'Tones|Ringtones|Soundtrack|Soundtrack',
4546             8298 => 'Tones|Ringtones|Soundtrack|TV Soundtrack',
4547             8299 => 'Tones|Ringtones|Vocal|Standards',
4548             8300 => 'Tones|Ringtones|Vocal|Traditional Pop',
4549             8301 => 'Tones|Ringtones|Vocal|Trot',
4550             8302 => 'Tones|Ringtones|Jazz|Vocal Jazz',
4551             8303 => 'Tones|Ringtones|Vocal|Vocal Pop',
4552             8304 => 'Tones|Ringtones|African',
4553             8305 => 'Tones|Ringtones|African|Afrikaans',
4554             8306 => 'Tones|Ringtones|African|Afro-Beat',
4555             8307 => 'Tones|Ringtones|African|Afro-Pop',
4556             8308 => 'Tones|Ringtones|Turkish|Arabesque',
4557             8309 => 'Tones|Ringtones|World|Asia',
4558             8310 => 'Tones|Ringtones|World|Australia',
4559             8311 => 'Tones|Ringtones|World|Cajun',
4560             8312 => 'Tones|Ringtones|World|Calypso',
4561             8313 => 'Tones|Ringtones|World|Caribbean',
4562             8314 => 'Tones|Ringtones|World|Celtic',
4563             8315 => 'Tones|Ringtones|World|Celtic Folk',
4564             8316 => 'Tones|Ringtones|World|Contemporary Celtic',
4565             8317 => 'Tones|Ringtones|World|Dangdut',
4566             8318 => 'Tones|Ringtones|World|Dini',
4567             8319 => 'Tones|Ringtones|World|Europe',
4568             8320 => 'Tones|Ringtones|World|Fado',
4569             8321 => 'Tones|Ringtones|World|Farsi',
4570             8322 => 'Tones|Ringtones|World|Flamenco',
4571             8323 => 'Tones|Ringtones|World|France',
4572             8324 => 'Tones|Ringtones|Turkish|Halk',
4573             8325 => 'Tones|Ringtones|World|Hawaii',
4574             8326 => 'Tones|Ringtones|World|Iberia',
4575             8327 => 'Tones|Ringtones|World|Indonesian Religious',
4576             8328 => 'Tones|Ringtones|World|Israeli',
4577             8329 => 'Tones|Ringtones|World|Japan',
4578             8330 => 'Tones|Ringtones|World|Klezmer',
4579             8331 => 'Tones|Ringtones|World|North America',
4580             8332 => 'Tones|Ringtones|World|Polka',
4581             8333 => 'Tones|Ringtones|Russian',
4582             8334 => 'Tones|Ringtones|Russian|Russian Chanson',
4583             8335 => 'Tones|Ringtones|Turkish|Sanat',
4584             8336 => 'Tones|Ringtones|World|Soca',
4585             8337 => 'Tones|Ringtones|World|South Africa',
4586             8338 => 'Tones|Ringtones|World|South America',
4587             8339 => 'Tones|Ringtones|World|Tango',
4588             8340 => 'Tones|Ringtones|World|Traditional Celtic',
4589             8341 => 'Tones|Ringtones|Turkish',
4590             8342 => 'Tones|Ringtones|World|Worldbeat',
4591             8343 => 'Tones|Ringtones|World|Zydeco',
4592             8345 => 'Tones|Ringtones|Classical|Art Song',
4593             8346 => 'Tones|Ringtones|Classical|Brass & Woodwinds',
4594             8347 => 'Tones|Ringtones|Classical|Solo Instrumental',
4595             8348 => 'Tones|Ringtones|Classical|Contemporary Era',
4596             8349 => 'Tones|Ringtones|Classical|Oratorio',
4597             8350 => 'Tones|Ringtones|Classical|Cantata',
4598             8351 => 'Tones|Ringtones|Classical|Electronic',
4599             8352 => 'Tones|Ringtones|Classical|Sacred',
4600             8353 => 'Tones|Ringtones|Classical|Guitar',
4601             8354 => 'Tones|Ringtones|Classical|Piano',
4602             8355 => 'Tones|Ringtones|Classical|Violin',
4603             8356 => 'Tones|Ringtones|Classical|Cello',
4604             8357 => 'Tones|Ringtones|Classical|Percussion',
4605             8358 => 'Tones|Ringtones|Electronic|Dubstep',
4606             8359 => 'Tones|Ringtones|Electronic|Bass',
4607             8360 => 'Tones|Ringtones|Hip-Hop/Rap|UK Hip Hop',
4608             8361 => 'Tones|Ringtones|Reggae|Lovers Rock',
4609             8362 => 'Tones|Ringtones|Alternative|EMO',
4610             8363 => 'Tones|Ringtones|Alternative|Pop Punk',
4611             8364 => 'Tones|Ringtones|Alternative|Indie Pop',
4612             8365 => 'Tones|Ringtones|New Age|Yoga',
4613             8366 => 'Tones|Ringtones|Pop|Tribute',
4614             8367 => 'Tones|Ringtones|Pop|Shows',
4615             8368 => 'Tones|Ringtones|Cuban',
4616             8369 => 'Tones|Ringtones|Cuban|Mambo',
4617             8370 => 'Tones|Ringtones|Cuban|Chachacha',
4618             8371 => 'Tones|Ringtones|Cuban|Guajira',
4619             8372 => 'Tones|Ringtones|Cuban|Son',
4620             8373 => 'Tones|Ringtones|Cuban|Bolero',
4621             8374 => 'Tones|Ringtones|Cuban|Guaracha',
4622             8375 => 'Tones|Ringtones|Cuban|Timba',
4623             8376 => 'Tones|Ringtones|Soundtrack|Video Game',
4624             8377 => 'Tones|Ringtones|Indian|Regional Indian|Punjabi|Punjabi Pop',
4625             8378 => 'Tones|Ringtones|Indian|Regional Indian|Bengali|Rabindra Sangeet',
4626             8379 => 'Tones|Ringtones|Indian|Regional Indian|Malayalam',
4627             8380 => 'Tones|Ringtones|Indian|Regional Indian|Kannada',
4628             8381 => 'Tones|Ringtones|Indian|Regional Indian|Marathi',
4629             8382 => 'Tones|Ringtones|Indian|Regional Indian|Gujarati',
4630             8383 => 'Tones|Ringtones|Indian|Regional Indian|Assamese',
4631             8384 => 'Tones|Ringtones|Indian|Regional Indian|Bhojpuri',
4632             8385 => 'Tones|Ringtones|Indian|Regional Indian|Haryanvi',
4633             8386 => 'Tones|Ringtones|Indian|Regional Indian|Odia',
4634             8387 => 'Tones|Ringtones|Indian|Regional Indian|Rajasthani',
4635             8388 => 'Tones|Ringtones|Indian|Regional Indian|Urdu',
4636             8389 => 'Tones|Ringtones|Indian|Regional Indian|Punjabi',
4637             8390 => 'Tones|Ringtones|Indian|Regional Indian|Bengali',
4638             8391 => 'Tones|Ringtones|Indian|Indian Classical|Carnatic Classical',
4639             8392 => 'Tones|Ringtones|Indian|Indian Classical|Hindustani Classical',
4640             8393 => 'Tones|Ringtones|African|Afro House',
4641             8394 => 'Tones|Ringtones|African|Afro Soul',
4642             8395 => 'Tones|Ringtones|African|Afrobeats',
4643             8396 => 'Tones|Ringtones|African|Benga',
4644             8397 => 'Tones|Ringtones|African|Bongo-Flava',
4645             8398 => 'Tones|Ringtones|African|Coupe-Decale',
4646             8399 => 'Tones|Ringtones|African|Gqom',
4647             8400 => 'Tones|Ringtones|African|Highlife',
4648             8401 => 'Tones|Ringtones|African|Kuduro',
4649             8402 => 'Tones|Ringtones|African|Kizomba',
4650             8403 => 'Tones|Ringtones|African|Kwaito',
4651             8404 => 'Tones|Ringtones|African|Mbalax',
4652             8405 => 'Tones|Ringtones|African|Ndombolo',
4653             8406 => 'Tones|Ringtones|African|Shangaan Electro',
4654             8407 => 'Tones|Ringtones|African|Soukous',
4655             8408 => 'Tones|Ringtones|African|Taarab',
4656             8409 => 'Tones|Ringtones|African|Zouglou',
4657             8410 => 'Tones|Ringtones|Turkish|Ozgun',
4658             8411 => 'Tones|Ringtones|Turkish|Fantezi',
4659             8412 => 'Tones|Ringtones|Turkish|Religious',
4660             8413 => 'Tones|Ringtones|Pop|Turkish Pop',
4661             8414 => 'Tones|Ringtones|Rock|Turkish Rock',
4662             8415 => 'Tones|Ringtones|Alternative|Turkish Alternative',
4663             8416 => 'Tones|Ringtones|Hip-Hop/Rap|Turkish Hip-Hop/Rap',
4664             8417 => 'Tones|Ringtones|African|Maskandi',
4665             8418 => 'Tones|Ringtones|Russian|Russian Romance',
4666             8419 => 'Tones|Ringtones|Russian|Russian Bard',
4667             8420 => 'Tones|Ringtones|Russian|Russian Pop',
4668             8421 => 'Tones|Ringtones|Russian|Russian Rock',
4669             8422 => 'Tones|Ringtones|Russian|Russian Hip-Hop',
4670             8423 => 'Tones|Ringtones|Arabic|Levant',
4671             8424 => 'Tones|Ringtones|Arabic|Levant|Dabke',
4672             8425 => 'Tones|Ringtones|Arabic|Maghreb Rai',
4673             8426 => 'Tones|Ringtones|Arabic|Khaleeji|Khaleeji Jalsat',
4674             8427 => 'Tones|Ringtones|Arabic|Khaleeji|Khaleeji Shailat',
4675             8428 => 'Tones|Ringtones|Tarab',
4676             8429 => 'Tones|Ringtones|Tarab|Iraqi Tarab',
4677             8430 => 'Tones|Ringtones|Tarab|Egyptian Tarab',
4678             8431 => 'Tones|Ringtones|Tarab|Khaleeji Tarab',
4679             8432 => 'Tones|Ringtones|Pop|Levant Pop',
4680             8433 => 'Tones|Ringtones|Pop|Iraqi Pop',
4681             8434 => 'Tones|Ringtones|Pop|Egyptian Pop',
4682             8435 => 'Tones|Ringtones|Pop|Maghreb Pop',
4683             8436 => 'Tones|Ringtones|Pop|Khaleeji Pop',
4684             8437 => 'Tones|Ringtones|Hip-Hop/Rap|Levant Hip-Hop',
4685             8438 => 'Tones|Ringtones|Hip-Hop/Rap|Egyptian Hip-Hop',
4686             8439 => 'Tones|Ringtones|Hip-Hop/Rap|Maghreb Hip-Hop',
4687             8440 => 'Tones|Ringtones|Hip-Hop/Rap|Khaleeji Hip-Hop',
4688             8441 => 'Tones|Ringtones|Alternative|Indie Levant',
4689             8442 => 'Tones|Ringtones|Alternative|Indie Egyptian',
4690             8443 => 'Tones|Ringtones|Alternative|Indie Maghreb',
4691             8444 => 'Tones|Ringtones|Electronic|Levant Electronic',
4692             8445 => "Tones|Ringtones|Electronic|Electro-Cha'abi",
4693             8446 => 'Tones|Ringtones|Electronic|Maghreb Electronic',
4694             8447 => 'Tones|Ringtones|Folk|Iraqi Folk',
4695             8448 => 'Tones|Ringtones|Folk|Khaleeji Folk',
4696             8449 => 'Tones|Ringtones|Dance|Maghreb Dance',
4697             9002 => 'Books|Nonfiction',
4698             9003 => 'Books|Romance',
4699             9004 => 'Books|Travel & Adventure',
4700             9007 => 'Books|Arts & Entertainment',
4701             9008 => 'Books|Biographies & Memoirs',
4702             9009 => 'Books|Business & Personal Finance',
4703             9010 => 'Books|Children & Teens',
4704             9012 => 'Books|Humor',
4705             9015 => 'Books|History',
4706             9018 => 'Books|Religion & Spirituality',
4707             9019 => 'Books|Science & Nature',
4708             9020 => 'Books|Sci-Fi & Fantasy',
4709             9024 => 'Books|Lifestyle & Home',
4710             9025 => 'Books|Self-Development',
4711             9026 => 'Books|Comics & Graphic Novels',
4712             9027 => 'Books|Computers & Internet',
4713             9028 => 'Books|Cookbooks, Food & Wine',
4714             9029 => 'Books|Professional & Technical',
4715             9030 => 'Books|Parenting',
4716             9031 => 'Books|Fiction & Literature',
4717             9032 => 'Books|Mysteries & Thrillers',
4718             9033 => 'Books|Reference',
4719             9034 => 'Books|Politics & Current Events',
4720             9035 => 'Books|Sports & Outdoors',
4721             10001 => 'Books|Lifestyle & Home|Antiques & Collectibles',
4722             10002 => 'Books|Arts & Entertainment|Art & Architecture',
4723             10003 => 'Books|Religion & Spirituality|Bibles',
4724             10004 => 'Books|Self-Development|Spirituality',
4725             10005 => 'Books|Business & Personal Finance|Industries & Professions',
4726             10006 => 'Books|Business & Personal Finance|Marketing & Sales',
4727             10007 => 'Books|Business & Personal Finance|Small Business & Entrepreneurship',
4728             10008 => 'Books|Business & Personal Finance|Personal Finance',
4729             10009 => 'Books|Business & Personal Finance|Reference',
4730             10010 => 'Books|Business & Personal Finance|Careers',
4731             10011 => 'Books|Business & Personal Finance|Economics',
4732             10012 => 'Books|Business & Personal Finance|Investing',
4733             10013 => 'Books|Business & Personal Finance|Finance',
4734             10014 => 'Books|Business & Personal Finance|Management & Leadership',
4735             10015 => 'Books|Comics & Graphic Novels|Graphic Novels',
4736             10016 => 'Books|Comics & Graphic Novels|Manga',
4737             10017 => 'Books|Computers & Internet|Computers',
4738             10018 => 'Books|Computers & Internet|Databases',
4739             10019 => 'Books|Computers & Internet|Digital Media',
4740             10020 => 'Books|Computers & Internet|Internet',
4741             10021 => 'Books|Computers & Internet|Network',
4742             10022 => 'Books|Computers & Internet|Operating Systems',
4743             10023 => 'Books|Computers & Internet|Programming',
4744             10024 => 'Books|Computers & Internet|Software',
4745             10025 => 'Books|Computers & Internet|System Administration',
4746             10026 => 'Books|Cookbooks, Food & Wine|Beverages',
4747             10027 => 'Books|Cookbooks, Food & Wine|Courses & Dishes',
4748             10028 => 'Books|Cookbooks, Food & Wine|Special Diet',
4749             10029 => 'Books|Cookbooks, Food & Wine|Special Occasions',
4750             10030 => 'Books|Cookbooks, Food & Wine|Methods',
4751             10031 => 'Books|Cookbooks, Food & Wine|Reference',
4752             10032 => 'Books|Cookbooks, Food & Wine|Regional & Ethnic',
4753             10033 => 'Books|Cookbooks, Food & Wine|Specific Ingredients',
4754             10034 => 'Books|Lifestyle & Home|Crafts & Hobbies',
4755             10035 => 'Books|Professional & Technical|Design',
4756             10036 => 'Books|Arts & Entertainment|Theater',
4757             10037 => 'Books|Professional & Technical|Education',
4758             10038 => 'Books|Nonfiction|Family & Relationships',
4759             10039 => 'Books|Fiction & Literature|Action & Adventure',
4760             10040 => 'Books|Fiction & Literature|African American',
4761             10041 => 'Books|Fiction & Literature|Religious',
4762             10042 => 'Books|Fiction & Literature|Classics',
4763             10043 => 'Books|Fiction & Literature|Erotica',
4764             10044 => 'Books|Sci-Fi & Fantasy|Fantasy',
4765             10045 => 'Books|Fiction & Literature|Gay',
4766             10046 => 'Books|Fiction & Literature|Ghost',
4767             10047 => 'Books|Fiction & Literature|Historical',
4768             10048 => 'Books|Fiction & Literature|Horror',
4769             10049 => 'Books|Fiction & Literature|Literary',
4770             10050 => 'Books|Mysteries & Thrillers|Hard-Boiled',
4771             10051 => 'Books|Mysteries & Thrillers|Historical',
4772             10052 => 'Books|Mysteries & Thrillers|Police Procedural',
4773             10053 => 'Books|Mysteries & Thrillers|Short Stories',
4774             10054 => 'Books|Mysteries & Thrillers|British Detectives',
4775             10055 => 'Books|Mysteries & Thrillers|Women Sleuths',
4776             10056 => 'Books|Romance|Erotic Romance',
4777             10057 => 'Books|Romance|Contemporary',
4778             10058 => 'Books|Romance|Paranormal',
4779             10059 => 'Books|Romance|Historical',
4780             10060 => 'Books|Romance|Short Stories',
4781             10061 => 'Books|Romance|Suspense',
4782             10062 => 'Books|Romance|Western',
4783             10063 => 'Books|Sci-Fi & Fantasy|Science Fiction',
4784             10064 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature',
4785             10065 => 'Books|Fiction & Literature|Short Stories',
4786             10066 => 'Books|Reference|Foreign Languages',
4787             10067 => 'Books|Arts & Entertainment|Games',
4788             10068 => 'Books|Lifestyle & Home|Gardening',
4789             10069 => 'Books|Self-Development|Health & Fitness',
4790             10070 => 'Books|History|Africa',
4791             10071 => 'Books|History|Americas',
4792             10072 => 'Books|History|Ancient',
4793             10073 => 'Books|History|Asia',
4794             10074 => 'Books|History|Australia & Oceania',
4795             10075 => 'Books|History|Europe',
4796             10076 => 'Books|History|Latin America',
4797             10077 => 'Books|History|Middle East',
4798             10078 => 'Books|History|Military',
4799             10079 => 'Books|History|United States',
4800             10080 => 'Books|History|World',
4801             10081 => "Books|Children & Teens|Children's Fiction",
4802             10082 => "Books|Children & Teens|Children's Nonfiction",
4803             10083 => 'Books|Professional & Technical|Law',
4804             10084 => 'Books|Fiction & Literature|Literary Criticism',
4805             10085 => 'Books|Science & Nature|Mathematics',
4806             10086 => 'Books|Professional & Technical|Medical',
4807             10087 => 'Books|Arts & Entertainment|Music',
4808             10088 => 'Books|Science & Nature|Nature',
4809             10089 => 'Books|Arts & Entertainment|Performing Arts',
4810             10090 => 'Books|Lifestyle & Home|Pets',
4811             10091 => 'Books|Nonfiction|Philosophy',
4812             10092 => 'Books|Arts & Entertainment|Photography',
4813             10093 => 'Books|Fiction & Literature|Poetry',
4814             10094 => 'Books|Self-Development|Psychology',
4815             10095 => 'Books|Reference|Almanacs & Yearbooks',
4816             10096 => 'Books|Reference|Atlases & Maps',
4817             10097 => 'Books|Reference|Catalogs & Directories',
4818             10098 => 'Books|Reference|Consumer Guides',
4819             10099 => 'Books|Reference|Dictionaries & Thesauruses',
4820             10100 => 'Books|Reference|Encyclopedias',
4821             10101 => 'Books|Reference|Etiquette',
4822             10102 => 'Books|Reference|Quotations',
4823             10103 => 'Books|Reference|Words & Language',
4824             10104 => 'Books|Reference|Writing',
4825             10105 => 'Books|Religion & Spirituality|Bible Studies',
4826             10106 => 'Books|Religion & Spirituality|Buddhism',
4827             10107 => 'Books|Religion & Spirituality|Christianity',
4828             10108 => 'Books|Religion & Spirituality|Hinduism',
4829             10109 => 'Books|Religion & Spirituality|Islam',
4830             10110 => 'Books|Religion & Spirituality|Judaism',
4831             10111 => 'Books|Science & Nature|Astronomy',
4832             10112 => 'Books|Science & Nature|Chemistry',
4833             10113 => 'Books|Science & Nature|Earth Sciences',
4834             10114 => 'Books|Science & Nature|Essays',
4835             10115 => 'Books|Science & Nature|History',
4836             10116 => 'Books|Science & Nature|Life Sciences',
4837             10117 => 'Books|Science & Nature|Physics',
4838             10118 => 'Books|Science & Nature|Reference',
4839             10119 => 'Books|Self-Development|Self-Improvement',
4840             10120 => 'Books|Nonfiction|Social Science',
4841             10121 => 'Books|Sports & Outdoors|Baseball',
4842             10122 => 'Books|Sports & Outdoors|Basketball',
4843             10123 => 'Books|Sports & Outdoors|Coaching',
4844             10124 => 'Books|Sports & Outdoors|Extreme Sports',
4845             10125 => 'Books|Sports & Outdoors|Football',
4846             10126 => 'Books|Sports & Outdoors|Golf',
4847             10127 => 'Books|Sports & Outdoors|Hockey',
4848             10128 => 'Books|Sports & Outdoors|Mountaineering',
4849             10129 => 'Books|Sports & Outdoors|Outdoors',
4850             10130 => 'Books|Sports & Outdoors|Racket Sports',
4851             10131 => 'Books|Sports & Outdoors|Reference',
4852             10132 => 'Books|Sports & Outdoors|Soccer',
4853             10133 => 'Books|Sports & Outdoors|Training',
4854             10134 => 'Books|Sports & Outdoors|Water Sports',
4855             10135 => 'Books|Sports & Outdoors|Winter Sports',
4856             10136 => 'Books|Reference|Study Aids',
4857             10137 => 'Books|Professional & Technical|Engineering',
4858             10138 => 'Books|Nonfiction|Transportation',
4859             10139 => 'Books|Travel & Adventure|Africa',
4860             10140 => 'Books|Travel & Adventure|Asia',
4861             10141 => 'Books|Travel & Adventure|Specialty Travel',
4862             10142 => 'Books|Travel & Adventure|Canada',
4863             10143 => 'Books|Travel & Adventure|Caribbean',
4864             10144 => 'Books|Travel & Adventure|Latin America',
4865             10145 => 'Books|Travel & Adventure|Essays & Memoirs',
4866             10146 => 'Books|Travel & Adventure|Europe',
4867             10147 => 'Books|Travel & Adventure|Middle East',
4868             10148 => 'Books|Travel & Adventure|United States',
4869             10149 => 'Books|Nonfiction|True Crime',
4870             11001 => 'Books|Sci-Fi & Fantasy|Fantasy|Contemporary',
4871             11002 => 'Books|Sci-Fi & Fantasy|Fantasy|Epic',
4872             11003 => 'Books|Sci-Fi & Fantasy|Fantasy|Historical',
4873             11004 => 'Books|Sci-Fi & Fantasy|Fantasy|Paranormal',
4874             11005 => 'Books|Sci-Fi & Fantasy|Fantasy|Short Stories',
4875             11006 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature|Adventure',
4876             11007 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature|High Tech',
4877             11008 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature|Short Stories',
4878             11009 => 'Books|Professional & Technical|Education|Language Arts & Disciplines',
4879             11010 => 'Books|Communications & Media',
4880             11011 => 'Books|Communications & Media|Broadcasting',
4881             11012 => 'Books|Communications & Media|Digital Media',
4882             11013 => 'Books|Communications & Media|Journalism',
4883             11014 => 'Books|Communications & Media|Photojournalism',
4884             11015 => 'Books|Communications & Media|Print',
4885             11016 => 'Books|Communications & Media|Speech',
4886             11017 => 'Books|Communications & Media|Writing',
4887             11018 => 'Books|Arts & Entertainment|Art & Architecture|Urban Planning',
4888             11019 => 'Books|Arts & Entertainment|Dance',
4889             11020 => 'Books|Arts & Entertainment|Fashion',
4890             11021 => 'Books|Arts & Entertainment|Film',
4891             11022 => 'Books|Arts & Entertainment|Interior Design',
4892             11023 => 'Books|Arts & Entertainment|Media Arts',
4893             11024 => 'Books|Arts & Entertainment|Radio',
4894             11025 => 'Books|Arts & Entertainment|TV',
4895             11026 => 'Books|Arts & Entertainment|Visual Arts',
4896             11027 => 'Books|Biographies & Memoirs|Arts & Entertainment',
4897             11028 => 'Books|Biographies & Memoirs|Business',
4898             11029 => 'Books|Biographies & Memoirs|Culinary',
4899             11030 => 'Books|Biographies & Memoirs|Gay & Lesbian',
4900             11031 => 'Books|Biographies & Memoirs|Historical',
4901             11032 => 'Books|Biographies & Memoirs|Literary',
4902             11033 => 'Books|Biographies & Memoirs|Media & Journalism',
4903             11034 => 'Books|Biographies & Memoirs|Military',
4904             11035 => 'Books|Biographies & Memoirs|Politics',
4905             11036 => 'Books|Biographies & Memoirs|Religious',
4906             11037 => 'Books|Biographies & Memoirs|Science & Technology',
4907             11038 => 'Books|Biographies & Memoirs|Sports',
4908             11039 => 'Books|Biographies & Memoirs|Women',
4909             11040 => 'Books|Romance|New Adult',
4910             11042 => 'Books|Romance|Romantic Comedy',
4911             11043 => 'Books|Romance|Gay & Lesbian',
4912             11044 => 'Books|Fiction & Literature|Essays',
4913             11045 => 'Books|Fiction & Literature|Anthologies',
4914             11046 => 'Books|Fiction & Literature|Comparative Literature',
4915             11047 => 'Books|Fiction & Literature|Drama',
4916             11049 => 'Books|Fiction & Literature|Fairy Tales, Myths & Fables',
4917             11050 => 'Books|Fiction & Literature|Family',
4918             11051 => 'Books|Comics & Graphic Novels|Manga|School Drama',
4919             11052 => 'Books|Comics & Graphic Novels|Manga|Human Drama',
4920             11053 => 'Books|Comics & Graphic Novels|Manga|Family Drama',
4921             11054 => 'Books|Sports & Outdoors|Boxing',
4922             11055 => 'Books|Sports & Outdoors|Cricket',
4923             11056 => 'Books|Sports & Outdoors|Cycling',
4924             11057 => 'Books|Sports & Outdoors|Equestrian',
4925             11058 => 'Books|Sports & Outdoors|Martial Arts & Self Defense',
4926             11059 => 'Books|Sports & Outdoors|Motor Sports',
4927             11060 => 'Books|Sports & Outdoors|Rugby',
4928             11061 => 'Books|Sports & Outdoors|Running',
4929             11062 => 'Books|Self-Development|Diet & Nutrition',
4930             11063 => 'Books|Science & Nature|Agriculture',
4931             11064 => 'Books|Science & Nature|Atmosphere',
4932             11065 => 'Books|Science & Nature|Biology',
4933             11066 => 'Books|Science & Nature|Ecology',
4934             11067 => 'Books|Science & Nature|Environment',
4935             11068 => 'Books|Science & Nature|Geography',
4936             11069 => 'Books|Science & Nature|Geology',
4937             11070 => 'Books|Nonfiction|Social Science|Anthropology',
4938             11071 => 'Books|Nonfiction|Social Science|Archaeology',
4939             11072 => 'Books|Nonfiction|Social Science|Civics',
4940             11073 => 'Books|Nonfiction|Social Science|Government',
4941             11074 => 'Books|Nonfiction|Social Science|Social Studies',
4942             11075 => 'Books|Nonfiction|Social Science|Social Welfare',
4943             11076 => 'Books|Nonfiction|Social Science|Society',
4944             11077 => 'Books|Nonfiction|Philosophy|Aesthetics',
4945             11078 => 'Books|Nonfiction|Philosophy|Epistemology',
4946             11079 => 'Books|Nonfiction|Philosophy|Ethics',
4947             11080 => 'Books|Nonfiction|Philosophy|Language',
4948             11081 => 'Books|Nonfiction|Philosophy|Logic',
4949             11082 => 'Books|Nonfiction|Philosophy|Metaphysics',
4950             11083 => 'Books|Nonfiction|Philosophy|Political',
4951             11084 => 'Books|Nonfiction|Philosophy|Religion',
4952             11085 => 'Books|Reference|Manuals',
4953             11086 => 'Books|Kids',
4954             11087 => 'Books|Kids|Animals',
4955             11088 => 'Books|Kids|Basic Concepts',
4956             11089 => 'Books|Kids|Basic Concepts|Alphabet',
4957             11090 => 'Books|Kids|Basic Concepts|Body',
4958             11091 => 'Books|Kids|Basic Concepts|Colors',
4959             11092 => 'Books|Kids|Basic Concepts|Counting & Numbers',
4960             11093 => 'Books|Kids|Basic Concepts|Date & Time',
4961             11094 => 'Books|Kids|Basic Concepts|General',
4962             11095 => 'Books|Kids|Basic Concepts|Money',
4963             11096 => 'Books|Kids|Basic Concepts|Opposites',
4964             11097 => 'Books|Kids|Basic Concepts|Seasons',
4965             11098 => 'Books|Kids|Basic Concepts|Senses & Sensation',
4966             11099 => 'Books|Kids|Basic Concepts|Size & Shape',
4967             11100 => 'Books|Kids|Basic Concepts|Sounds',
4968             11101 => 'Books|Kids|Basic Concepts|Words',
4969             11102 => 'Books|Kids|Biography',
4970             11103 => 'Books|Kids|Careers & Occupations',
4971             11104 => 'Books|Kids|Computers & Technology',
4972             11105 => 'Books|Kids|Cooking & Food',
4973             11106 => 'Books|Kids|Arts & Entertainment',
4974             11107 => 'Books|Kids|Arts & Entertainment|Art',
4975             11108 => 'Books|Kids|Arts & Entertainment|Crafts',
4976             11109 => 'Books|Kids|Arts & Entertainment|Music',
4977             11110 => 'Books|Kids|Arts & Entertainment|Performing Arts',
4978             11111 => 'Books|Kids|Family',
4979             11112 => 'Books|Kids|Fiction',
4980             11113 => 'Books|Kids|Fiction|Action & Adventure',
4981             11114 => 'Books|Kids|Fiction|Animals',
4982             11115 => 'Books|Kids|Fiction|Classics',
4983             11116 => 'Books|Kids|Fiction|Comics & Graphic Novels',
4984             11117 => 'Books|Kids|Fiction|Culture, Places & People',
4985             11118 => 'Books|Kids|Fiction|Family & Relationships',
4986             11119 => 'Books|Kids|Fiction|Fantasy',
4987             11120 => 'Books|Kids|Fiction|Fairy Tales, Myths & Fables',
4988             11121 => 'Books|Kids|Fiction|Favorite Characters',
4989             11122 => 'Books|Kids|Fiction|Historical',
4990             11123 => 'Books|Kids|Fiction|Holidays & Celebrations',
4991             11124 => 'Books|Kids|Fiction|Monsters & Ghosts',
4992             11125 => 'Books|Kids|Fiction|Mysteries',
4993             11126 => 'Books|Kids|Fiction|Nature',
4994             11127 => 'Books|Kids|Fiction|Religion',
4995             11128 => 'Books|Kids|Fiction|Sci-Fi',
4996             11129 => 'Books|Kids|Fiction|Social Issues',
4997             11130 => 'Books|Kids|Fiction|Sports & Recreation',
4998             11131 => 'Books|Kids|Fiction|Transportation',
4999             11132 => 'Books|Kids|Games & Activities',
5000             11133 => 'Books|Kids|General Nonfiction',
5001             11134 => 'Books|Kids|Health',
5002             11135 => 'Books|Kids|History',
5003             11136 => 'Books|Kids|Holidays & Celebrations',
5004             11137 => 'Books|Kids|Holidays & Celebrations|Birthdays',
5005             11138 => 'Books|Kids|Holidays & Celebrations|Christmas & Advent',
5006             11139 => 'Books|Kids|Holidays & Celebrations|Easter & Lent',
5007             11140 => 'Books|Kids|Holidays & Celebrations|General',
5008             11141 => 'Books|Kids|Holidays & Celebrations|Halloween',
5009             11142 => 'Books|Kids|Holidays & Celebrations|Hanukkah',
5010             11143 => 'Books|Kids|Holidays & Celebrations|Other',
5011             11144 => 'Books|Kids|Holidays & Celebrations|Passover',
5012             11145 => 'Books|Kids|Holidays & Celebrations|Patriotic Holidays',
5013             11146 => 'Books|Kids|Holidays & Celebrations|Ramadan',
5014             11147 => 'Books|Kids|Holidays & Celebrations|Thanksgiving',
5015             11148 => "Books|Kids|Holidays & Celebrations|Valentine's Day",
5016             11149 => 'Books|Kids|Humor',
5017             11150 => 'Books|Kids|Humor|Jokes & Riddles',
5018             11151 => 'Books|Kids|Poetry',
5019             11152 => 'Books|Kids|Learning to Read',
5020             11153 => 'Books|Kids|Learning to Read|Chapter Books',
5021             11154 => 'Books|Kids|Learning to Read|Early Readers',
5022             11155 => 'Books|Kids|Learning to Read|Intermediate Readers',
5023             11156 => 'Books|Kids|Nursery Rhymes',
5024             11157 => 'Books|Kids|Government',
5025             11158 => 'Books|Kids|Reference',
5026             11159 => 'Books|Kids|Religion',
5027             11160 => 'Books|Kids|Science & Nature',
5028             11161 => 'Books|Kids|Social Issues',
5029             11162 => 'Books|Kids|Social Studies',
5030             11163 => 'Books|Kids|Sports & Recreation',
5031             11164 => 'Books|Kids|Transportation',
5032             11165 => 'Books|Young Adult',
5033             11166 => 'Books|Young Adult|Animals',
5034             11167 => 'Books|Young Adult|Biography',
5035             11168 => 'Books|Young Adult|Careers & Occupations',
5036             11169 => 'Books|Young Adult|Computers & Technology',
5037             11170 => 'Books|Young Adult|Cooking & Food',
5038             11171 => 'Books|Young Adult|Arts & Entertainment',
5039             11172 => 'Books|Young Adult|Arts & Entertainment|Art',
5040             11173 => 'Books|Young Adult|Arts & Entertainment|Crafts',
5041             11174 => 'Books|Young Adult|Arts & Entertainment|Music',
5042             11175 => 'Books|Young Adult|Arts & Entertainment|Performing Arts',
5043             11176 => 'Books|Young Adult|Family',
5044             11177 => 'Books|Young Adult|Fiction',
5045             11178 => 'Books|Young Adult|Fiction|Action & Adventure',
5046             11179 => 'Books|Young Adult|Fiction|Animals',
5047             11180 => 'Books|Young Adult|Fiction|Classics',
5048             11181 => 'Books|Young Adult|Fiction|Comics & Graphic Novels',
5049             11182 => 'Books|Young Adult|Fiction|Culture, Places & People',
5050             11183 => 'Books|Young Adult|Fiction|Dystopian',
5051             11184 => 'Books|Young Adult|Fiction|Family & Relationships',
5052             11185 => 'Books|Young Adult|Fiction|Fantasy',
5053             11186 => 'Books|Young Adult|Fiction|Fairy Tales, Myths & Fables',
5054             11187 => 'Books|Young Adult|Fiction|Favorite Characters',
5055             11188 => 'Books|Young Adult|Fiction|Historical',
5056             11189 => 'Books|Young Adult|Fiction|Holidays & Celebrations',
5057             11190 => 'Books|Young Adult|Fiction|Horror, Monsters & Ghosts',
5058             11191 => 'Books|Young Adult|Fiction|Crime & Mystery',
5059             11192 => 'Books|Young Adult|Fiction|Nature',
5060             11193 => 'Books|Young Adult|Fiction|Religion',
5061             11194 => 'Books|Young Adult|Fiction|Romance',
5062             11195 => 'Books|Young Adult|Fiction|Sci-Fi',
5063             11196 => 'Books|Young Adult|Fiction|Coming of Age',
5064             11197 => 'Books|Young Adult|Fiction|Sports & Recreation',
5065             11198 => 'Books|Young Adult|Fiction|Transportation',
5066             11199 => 'Books|Young Adult|Games & Activities',
5067             11200 => 'Books|Young Adult|General Nonfiction',
5068             11201 => 'Books|Young Adult|Health',
5069             11202 => 'Books|Young Adult|History',
5070             11203 => 'Books|Young Adult|Holidays & Celebrations',
5071             11204 => 'Books|Young Adult|Holidays & Celebrations|Birthdays',
5072             11205 => 'Books|Young Adult|Holidays & Celebrations|Christmas & Advent',
5073             11206 => 'Books|Young Adult|Holidays & Celebrations|Easter & Lent',
5074             11207 => 'Books|Young Adult|Holidays & Celebrations|General',
5075             11208 => 'Books|Young Adult|Holidays & Celebrations|Halloween',
5076             11209 => 'Books|Young Adult|Holidays & Celebrations|Hanukkah',
5077             11210 => 'Books|Young Adult|Holidays & Celebrations|Other',
5078             11211 => 'Books|Young Adult|Holidays & Celebrations|Passover',
5079             11212 => 'Books|Young Adult|Holidays & Celebrations|Patriotic Holidays',
5080             11213 => 'Books|Young Adult|Holidays & Celebrations|Ramadan',
5081             11214 => 'Books|Young Adult|Holidays & Celebrations|Thanksgiving',
5082             11215 => "Books|Young Adult|Holidays & Celebrations|Valentine's Day",
5083             11216 => 'Books|Young Adult|Humor',
5084             11217 => 'Books|Young Adult|Humor|Jokes & Riddles',
5085             11218 => 'Books|Young Adult|Poetry',
5086             11219 => 'Books|Young Adult|Politics & Government',
5087             11220 => 'Books|Young Adult|Reference',
5088             11221 => 'Books|Young Adult|Religion',
5089             11222 => 'Books|Young Adult|Science & Nature',
5090             11223 => 'Books|Young Adult|Coming of Age',
5091             11224 => 'Books|Young Adult|Social Studies',
5092             11225 => 'Books|Young Adult|Sports & Recreation',
5093             11226 => 'Books|Young Adult|Transportation',
5094             11227 => 'Books|Communications & Media',
5095             11228 => 'Books|Military & Warfare',
5096             11229 => 'Books|Romance|Inspirational',
5097             11231 => 'Books|Romance|Holiday',
5098             11232 => 'Books|Romance|Wholesome',
5099             11233 => 'Books|Romance|Military',
5100             11234 => 'Books|Arts & Entertainment|Art History',
5101             11236 => 'Books|Arts & Entertainment|Design',
5102             11243 => 'Books|Business & Personal Finance|Accounting',
5103             11244 => 'Books|Business & Personal Finance|Hospitality',
5104             11245 => 'Books|Business & Personal Finance|Real Estate',
5105             11246 => 'Books|Humor|Jokes & Riddles',
5106             11247 => 'Books|Religion & Spirituality|Comparative Religion',
5107             11255 => 'Books|Cookbooks, Food & Wine|Culinary Arts',
5108             11259 => 'Books|Mysteries & Thrillers|Cozy',
5109             11260 => 'Books|Politics & Current Events|Current Events',
5110             11261 => 'Books|Politics & Current Events|Foreign Policy & International Relations',
5111             11262 => 'Books|Politics & Current Events|Local Government',
5112             11263 => 'Books|Politics & Current Events|National Government',
5113             11264 => 'Books|Politics & Current Events|Political Science',
5114             11265 => 'Books|Politics & Current Events|Public Administration',
5115             11266 => 'Books|Politics & Current Events|World Affairs',
5116             11273 => 'Books|Nonfiction|Family & Relationships|Family & Childcare',
5117             11274 => 'Books|Nonfiction|Family & Relationships|Love & Romance',
5118             11275 => 'Books|Sci-Fi & Fantasy|Fantasy|Urban',
5119             11276 => 'Books|Reference|Foreign Languages|Arabic',
5120             11277 => 'Books|Reference|Foreign Languages|Bilingual Editions',
5121             11278 => 'Books|Reference|Foreign Languages|African Languages',
5122             11279 => 'Books|Reference|Foreign Languages|Ancient Languages',
5123             11280 => 'Books|Reference|Foreign Languages|Chinese',
5124             11281 => 'Books|Reference|Foreign Languages|English',
5125             11282 => 'Books|Reference|Foreign Languages|French',
5126             11283 => 'Books|Reference|Foreign Languages|German',
5127             11284 => 'Books|Reference|Foreign Languages|Hebrew',
5128             11285 => 'Books|Reference|Foreign Languages|Hindi',
5129             11286 => 'Books|Reference|Foreign Languages|Italian',
5130             11287 => 'Books|Reference|Foreign Languages|Japanese',
5131             11288 => 'Books|Reference|Foreign Languages|Korean',
5132             11289 => 'Books|Reference|Foreign Languages|Linguistics',
5133             11290 => 'Books|Reference|Foreign Languages|Other Languages',
5134             11291 => 'Books|Reference|Foreign Languages|Portuguese',
5135             11292 => 'Books|Reference|Foreign Languages|Russian',
5136             11293 => 'Books|Reference|Foreign Languages|Spanish',
5137             11294 => 'Books|Reference|Foreign Languages|Speech Pathology',
5138             11295 => 'Books|Science & Nature|Mathematics|Advanced Mathematics',
5139             11296 => 'Books|Science & Nature|Mathematics|Algebra',
5140             11297 => 'Books|Science & Nature|Mathematics|Arithmetic',
5141             11298 => 'Books|Science & Nature|Mathematics|Calculus',
5142             11299 => 'Books|Science & Nature|Mathematics|Geometry',
5143             11300 => 'Books|Science & Nature|Mathematics|Statistics',
5144             11301 => 'Books|Professional & Technical|Medical|Veterinary',
5145             11302 => 'Books|Professional & Technical|Medical|Neuroscience',
5146             11303 => 'Books|Professional & Technical|Medical|Immunology',
5147             11304 => 'Books|Professional & Technical|Medical|Nursing',
5148             11305 => 'Books|Professional & Technical|Medical|Pharmacology & Toxicology',
5149             11306 => 'Books|Professional & Technical|Medical|Anatomy & Physiology',
5150             11307 => 'Books|Professional & Technical|Medical|Dentistry',
5151             11308 => 'Books|Professional & Technical|Medical|Emergency Medicine',
5152             11309 => 'Books|Professional & Technical|Medical|Genetics',
5153             11310 => 'Books|Professional & Technical|Medical|Psychiatry',
5154             11311 => 'Books|Professional & Technical|Medical|Radiology',
5155             11312 => 'Books|Professional & Technical|Medical|Alternative Medicine',
5156             11317 => 'Books|Nonfiction|Philosophy|Political Philosophy',
5157             11319 => 'Books|Nonfiction|Philosophy|Philosophy of Language',
5158             11320 => 'Books|Nonfiction|Philosophy|Philosophy of Religion',
5159             11327 => 'Books|Nonfiction|Social Science|Sociology',
5160             11329 => 'Books|Professional & Technical|Engineering|Aeronautics',
5161             11330 => 'Books|Professional & Technical|Engineering|Chemical & Petroleum Engineering',
5162             11331 => 'Books|Professional & Technical|Engineering|Civil Engineering',
5163             11332 => 'Books|Professional & Technical|Engineering|Computer Science',
5164             11333 => 'Books|Professional & Technical|Engineering|Electrical Engineering',
5165             11334 => 'Books|Professional & Technical|Engineering|Environmental Engineering',
5166             11335 => 'Books|Professional & Technical|Engineering|Mechanical Engineering',
5167             11336 => 'Books|Professional & Technical|Engineering|Power Resources',
5168             11337 => 'Books|Comics & Graphic Novels|Manga|Boys',
5169             11338 => 'Books|Comics & Graphic Novels|Manga|Men',
5170             11339 => 'Books|Comics & Graphic Novels|Manga|Girls',
5171             11340 => 'Books|Comics & Graphic Novels|Manga|Women',
5172             11341 => 'Books|Comics & Graphic Novels|Manga|Other',
5173             11342 => 'Books|Comics & Graphic Novels|Manga|Yaoi',
5174             11343 => 'Books|Comics & Graphic Novels|Manga|Comic Essays',
5175             12001 => 'Mac App Store|Business',
5176             12002 => 'Mac App Store|Developer Tools',
5177             12003 => 'Mac App Store|Education',
5178             12004 => 'Mac App Store|Entertainment',
5179             12005 => 'Mac App Store|Finance',
5180             12006 => 'Mac App Store|Games',
5181             12007 => 'Mac App Store|Health & Fitness',
5182             12008 => 'Mac App Store|Lifestyle',
5183             12010 => 'Mac App Store|Medical',
5184             12011 => 'Mac App Store|Music',
5185             12012 => 'Mac App Store|News',
5186             12013 => 'Mac App Store|Photography',
5187             12014 => 'Mac App Store|Productivity',
5188             12015 => 'Mac App Store|Reference',
5189             12016 => 'Mac App Store|Social Networking',
5190             12017 => 'Mac App Store|Sports',
5191             12018 => 'Mac App Store|Travel',
5192             12019 => 'Mac App Store|Utilities',
5193             12020 => 'Mac App Store|Video',
5194             12021 => 'Mac App Store|Weather',
5195             12022 => 'Mac App Store|Graphics & Design',
5196             12201 => 'Mac App Store|Games|Action',
5197             12202 => 'Mac App Store|Games|Adventure',
5198             12203 => 'Mac App Store|Games|Casual',
5199             12204 => 'Mac App Store|Games|Board',
5200             12205 => 'Mac App Store|Games|Card',
5201             12206 => 'Mac App Store|Games|Casino',
5202             12207 => 'Mac App Store|Games|Dice',
5203             12208 => 'Mac App Store|Games|Educational',
5204             12209 => 'Mac App Store|Games|Family',
5205             12210 => 'Mac App Store|Games|Kids',
5206             12211 => 'Mac App Store|Games|Music',
5207             12212 => 'Mac App Store|Games|Puzzle',
5208             12213 => 'Mac App Store|Games|Racing',
5209             12214 => 'Mac App Store|Games|Role Playing',
5210             12215 => 'Mac App Store|Games|Simulation',
5211             12216 => 'Mac App Store|Games|Sports',
5212             12217 => 'Mac App Store|Games|Strategy',
5213             12218 => 'Mac App Store|Games|Trivia',
5214             12219 => 'Mac App Store|Games|Word',
5215             13001 => 'App Store|Magazines & Newspapers|News & Politics',
5216             13002 => 'App Store|Magazines & Newspapers|Fashion & Style',
5217             13003 => 'App Store|Magazines & Newspapers|Home & Garden',
5218             13004 => 'App Store|Magazines & Newspapers|Outdoors & Nature',
5219             13005 => 'App Store|Magazines & Newspapers|Sports & Leisure',
5220             13006 => 'App Store|Magazines & Newspapers|Automotive',
5221             13007 => 'App Store|Magazines & Newspapers|Arts & Photography',
5222             13008 => 'App Store|Magazines & Newspapers|Brides & Weddings',
5223             13009 => 'App Store|Magazines & Newspapers|Business & Investing',
5224             13010 => "App Store|Magazines & Newspapers|Children's Magazines",
5225             13011 => 'App Store|Magazines & Newspapers|Computers & Internet',
5226             13012 => 'App Store|Magazines & Newspapers|Cooking, Food & Drink',
5227             13013 => 'App Store|Magazines & Newspapers|Crafts & Hobbies',
5228             13014 => 'App Store|Magazines & Newspapers|Electronics & Audio',
5229             13015 => 'App Store|Magazines & Newspapers|Entertainment',
5230             13017 => 'App Store|Magazines & Newspapers|Health, Mind & Body',
5231             13018 => 'App Store|Magazines & Newspapers|History',
5232             13019 => 'App Store|Magazines & Newspapers|Literary Magazines & Journals',
5233             13020 => "App Store|Magazines & Newspapers|Men's Interest",
5234             13021 => 'App Store|Magazines & Newspapers|Movies & Music',
5235             13023 => 'App Store|Magazines & Newspapers|Parenting & Family',
5236             13024 => 'App Store|Magazines & Newspapers|Pets',
5237             13025 => 'App Store|Magazines & Newspapers|Professional & Trade',
5238             13026 => 'App Store|Magazines & Newspapers|Regional News',
5239             13027 => 'App Store|Magazines & Newspapers|Science',
5240             13028 => 'App Store|Magazines & Newspapers|Teens',
5241             13029 => 'App Store|Magazines & Newspapers|Travel & Regional',
5242             13030 => "App Store|Magazines & Newspapers|Women's Interest",
5243             15000 => 'Textbooks|Arts & Entertainment',
5244             15001 => 'Textbooks|Arts & Entertainment|Art & Architecture',
5245             15002 => 'Textbooks|Arts & Entertainment|Art & Architecture|Urban Planning',
5246             15003 => 'Textbooks|Arts & Entertainment|Art History',
5247             15004 => 'Textbooks|Arts & Entertainment|Dance',
5248             15005 => 'Textbooks|Arts & Entertainment|Design',
5249             15006 => 'Textbooks|Arts & Entertainment|Fashion',
5250             15007 => 'Textbooks|Arts & Entertainment|Film',
5251             15008 => 'Textbooks|Arts & Entertainment|Games',
5252             15009 => 'Textbooks|Arts & Entertainment|Interior Design',
5253             15010 => 'Textbooks|Arts & Entertainment|Media Arts',
5254             15011 => 'Textbooks|Arts & Entertainment|Music',
5255             15012 => 'Textbooks|Arts & Entertainment|Performing Arts',
5256             15013 => 'Textbooks|Arts & Entertainment|Photography',
5257             15014 => 'Textbooks|Arts & Entertainment|Theater',
5258             15015 => 'Textbooks|Arts & Entertainment|TV',
5259             15016 => 'Textbooks|Arts & Entertainment|Visual Arts',
5260             15017 => 'Textbooks|Biographies & Memoirs',
5261             15018 => 'Textbooks|Business & Personal Finance',
5262             15019 => 'Textbooks|Business & Personal Finance|Accounting',
5263             15020 => 'Textbooks|Business & Personal Finance|Careers',
5264             15021 => 'Textbooks|Business & Personal Finance|Economics',
5265             15022 => 'Textbooks|Business & Personal Finance|Finance',
5266             15023 => 'Textbooks|Business & Personal Finance|Hospitality',
5267             15024 => 'Textbooks|Business & Personal Finance|Industries & Professions',
5268             15025 => 'Textbooks|Business & Personal Finance|Investing',
5269             15026 => 'Textbooks|Business & Personal Finance|Management & Leadership',
5270             15027 => 'Textbooks|Business & Personal Finance|Marketing & Sales',
5271             15028 => 'Textbooks|Business & Personal Finance|Personal Finance',
5272             15029 => 'Textbooks|Business & Personal Finance|Real Estate',
5273             15030 => 'Textbooks|Business & Personal Finance|Reference',
5274             15031 => 'Textbooks|Business & Personal Finance|Small Business & Entrepreneurship',
5275             15032 => 'Textbooks|Children & Teens',
5276             15033 => 'Textbooks|Children & Teens|Fiction',
5277             15034 => 'Textbooks|Children & Teens|Nonfiction',
5278             15035 => 'Textbooks|Comics & Graphic Novels',
5279             15036 => 'Textbooks|Comics & Graphic Novels|Graphic Novels',
5280             15037 => 'Textbooks|Comics & Graphic Novels|Manga',
5281             15038 => 'Textbooks|Communications & Media',
5282             15039 => 'Textbooks|Communications & Media|Broadcasting',
5283             15040 => 'Textbooks|Communications & Media|Digital Media',
5284             15041 => 'Textbooks|Communications & Media|Journalism',
5285             15042 => 'Textbooks|Communications & Media|Photojournalism',
5286             15043 => 'Textbooks|Communications & Media|Print',
5287             15044 => 'Textbooks|Communications & Media|Speech',
5288             15045 => 'Textbooks|Communications & Media|Writing',
5289             15046 => 'Textbooks|Computers & Internet',
5290             15047 => 'Textbooks|Computers & Internet|Computers',
5291             15048 => 'Textbooks|Computers & Internet|Databases',
5292             15049 => 'Textbooks|Computers & Internet|Digital Media',
5293             15050 => 'Textbooks|Computers & Internet|Internet',
5294             15051 => 'Textbooks|Computers & Internet|Network',
5295             15052 => 'Textbooks|Computers & Internet|Operating Systems',
5296             15053 => 'Textbooks|Computers & Internet|Programming',
5297             15054 => 'Textbooks|Computers & Internet|Software',
5298             15055 => 'Textbooks|Computers & Internet|System Administration',
5299             15056 => 'Textbooks|Cookbooks, Food & Wine',
5300             15057 => 'Textbooks|Cookbooks, Food & Wine|Beverages',
5301             15058 => 'Textbooks|Cookbooks, Food & Wine|Courses & Dishes',
5302             15059 => 'Textbooks|Cookbooks, Food & Wine|Culinary Arts',
5303             15060 => 'Textbooks|Cookbooks, Food & Wine|Methods',
5304             15061 => 'Textbooks|Cookbooks, Food & Wine|Reference',
5305             15062 => 'Textbooks|Cookbooks, Food & Wine|Regional & Ethnic',
5306             15063 => 'Textbooks|Cookbooks, Food & Wine|Special Diet',
5307             15064 => 'Textbooks|Cookbooks, Food & Wine|Special Occasions',
5308             15065 => 'Textbooks|Cookbooks, Food & Wine|Specific Ingredients',
5309             15066 => 'Textbooks|Engineering',
5310             15067 => 'Textbooks|Engineering|Aeronautics',
5311             15068 => 'Textbooks|Engineering|Chemical & Petroleum Engineering',
5312             15069 => 'Textbooks|Engineering|Civil Engineering',
5313             15070 => 'Textbooks|Engineering|Computer Science',
5314             15071 => 'Textbooks|Engineering|Electrical Engineering',
5315             15072 => 'Textbooks|Engineering|Environmental Engineering',
5316             15073 => 'Textbooks|Engineering|Mechanical Engineering',
5317             15074 => 'Textbooks|Engineering|Power Resources',
5318             15075 => 'Textbooks|Fiction & Literature',
5319             15076 => 'Textbooks|Fiction & Literature|Latino',
5320             15077 => 'Textbooks|Fiction & Literature|Action & Adventure',
5321             15078 => 'Textbooks|Fiction & Literature|African American',
5322             15079 => 'Textbooks|Fiction & Literature|Anthologies',
5323             15080 => 'Textbooks|Fiction & Literature|Classics',
5324             15081 => 'Textbooks|Fiction & Literature|Comparative Literature',
5325             15082 => 'Textbooks|Fiction & Literature|Erotica',
5326             15083 => 'Textbooks|Fiction & Literature|Gay',
5327             15084 => 'Textbooks|Fiction & Literature|Ghost',
5328             15085 => 'Textbooks|Fiction & Literature|Historical',
5329             15086 => 'Textbooks|Fiction & Literature|Horror',
5330             15087 => 'Textbooks|Fiction & Literature|Literary',
5331             15088 => 'Textbooks|Fiction & Literature|Literary Criticism',
5332             15089 => 'Textbooks|Fiction & Literature|Poetry',
5333             15090 => 'Textbooks|Fiction & Literature|Religious',
5334             15091 => 'Textbooks|Fiction & Literature|Short Stories',
5335             15092 => 'Textbooks|Health, Mind & Body',
5336             15093 => 'Textbooks|Health, Mind & Body|Fitness',
5337             15094 => 'Textbooks|Health, Mind & Body|Self-Improvement',
5338             15095 => 'Textbooks|History',
5339             15096 => 'Textbooks|History|Africa',
5340             15097 => 'Textbooks|History|Americas',
5341             15098 => 'Textbooks|History|Americas|Canada',
5342             15099 => 'Textbooks|History|Americas|Latin America',
5343             15100 => 'Textbooks|History|Americas|United States',
5344             15101 => 'Textbooks|History|Ancient',
5345             15102 => 'Textbooks|History|Asia',
5346             15103 => 'Textbooks|History|Australia & Oceania',
5347             15104 => 'Textbooks|History|Europe',
5348             15105 => 'Textbooks|History|Middle East',
5349             15106 => 'Textbooks|History|Military',
5350             15107 => 'Textbooks|History|World',
5351             15108 => 'Textbooks|Humor',
5352             15109 => 'Textbooks|Language Studies',
5353             15110 => 'Textbooks|Language Studies|African Languages',
5354             15111 => 'Textbooks|Language Studies|Ancient Languages',
5355             15112 => 'Textbooks|Language Studies|Arabic',
5356             15113 => 'Textbooks|Language Studies|Bilingual Editions',
5357             15114 => 'Textbooks|Language Studies|Chinese',
5358             15115 => 'Textbooks|Language Studies|English',
5359             15116 => 'Textbooks|Language Studies|French',
5360             15117 => 'Textbooks|Language Studies|German',
5361             15118 => 'Textbooks|Language Studies|Hebrew',
5362             15119 => 'Textbooks|Language Studies|Hindi',
5363             15120 => 'Textbooks|Language Studies|Indigenous Languages',
5364             15121 => 'Textbooks|Language Studies|Italian',
5365             15122 => 'Textbooks|Language Studies|Japanese',
5366             15123 => 'Textbooks|Language Studies|Korean',
5367             15124 => 'Textbooks|Language Studies|Linguistics',
5368             15125 => 'Textbooks|Language Studies|Other Language',
5369             15126 => 'Textbooks|Language Studies|Portuguese',
5370             15127 => 'Textbooks|Language Studies|Russian',
5371             15128 => 'Textbooks|Language Studies|Spanish',
5372             15129 => 'Textbooks|Language Studies|Speech Pathology',
5373             15130 => 'Textbooks|Lifestyle & Home',
5374             15131 => 'Textbooks|Lifestyle & Home|Antiques & Collectibles',
5375             15132 => 'Textbooks|Lifestyle & Home|Crafts & Hobbies',
5376             15133 => 'Textbooks|Lifestyle & Home|Gardening',
5377             15134 => 'Textbooks|Lifestyle & Home|Pets',
5378             15135 => 'Textbooks|Mathematics',
5379             15136 => 'Textbooks|Mathematics|Advanced Mathematics',
5380             15137 => 'Textbooks|Mathematics|Algebra',
5381             15138 => 'Textbooks|Mathematics|Arithmetic',
5382             15139 => 'Textbooks|Mathematics|Calculus',
5383             15140 => 'Textbooks|Mathematics|Geometry',
5384             15141 => 'Textbooks|Mathematics|Statistics',
5385             15142 => 'Textbooks|Medicine',
5386             15143 => 'Textbooks|Medicine|Anatomy & Physiology',
5387             15144 => 'Textbooks|Medicine|Dentistry',
5388             15145 => 'Textbooks|Medicine|Emergency Medicine',
5389             15146 => 'Textbooks|Medicine|Genetics',
5390             15147 => 'Textbooks|Medicine|Immunology',
5391             15148 => 'Textbooks|Medicine|Neuroscience',
5392             15149 => 'Textbooks|Medicine|Nursing',
5393             15150 => 'Textbooks|Medicine|Pharmacology & Toxicology',
5394             15151 => 'Textbooks|Medicine|Psychiatry',
5395             15152 => 'Textbooks|Medicine|Psychology',
5396             15153 => 'Textbooks|Medicine|Radiology',
5397             15154 => 'Textbooks|Medicine|Veterinary',
5398             15155 => 'Textbooks|Mysteries & Thrillers',
5399             15156 => 'Textbooks|Mysteries & Thrillers|British Detectives',
5400             15157 => 'Textbooks|Mysteries & Thrillers|Hard-Boiled',
5401             15158 => 'Textbooks|Mysteries & Thrillers|Historical',
5402             15159 => 'Textbooks|Mysteries & Thrillers|Police Procedural',
5403             15160 => 'Textbooks|Mysteries & Thrillers|Short Stories',
5404             15161 => 'Textbooks|Mysteries & Thrillers|Women Sleuths',
5405             15162 => 'Textbooks|Nonfiction',
5406             15163 => 'Textbooks|Nonfiction|Family & Relationships',
5407             15164 => 'Textbooks|Nonfiction|Transportation',
5408             15165 => 'Textbooks|Nonfiction|True Crime',
5409             15166 => 'Textbooks|Parenting',
5410             15167 => 'Textbooks|Philosophy',
5411             15168 => 'Textbooks|Philosophy|Aesthetics',
5412             15169 => 'Textbooks|Philosophy|Epistemology',
5413             15170 => 'Textbooks|Philosophy|Ethics',
5414             15171 => 'Textbooks|Philosophy|Philosophy of Language',
5415             15172 => 'Textbooks|Philosophy|Logic',
5416             15173 => 'Textbooks|Philosophy|Metaphysics',
5417             15174 => 'Textbooks|Philosophy|Political Philosophy',
5418             15175 => 'Textbooks|Philosophy|Philosophy of Religion',
5419             15176 => 'Textbooks|Politics & Current Events',
5420             15177 => 'Textbooks|Politics & Current Events|Current Events',
5421             15178 => 'Textbooks|Politics & Current Events|Foreign Policy & International Relations',
5422             15179 => 'Textbooks|Politics & Current Events|Local Governments',
5423             15180 => 'Textbooks|Politics & Current Events|National Governments',
5424             15181 => 'Textbooks|Politics & Current Events|Political Science',
5425             15182 => 'Textbooks|Politics & Current Events|Public Administration',
5426             15183 => 'Textbooks|Politics & Current Events|World Affairs',
5427             15184 => 'Textbooks|Professional & Technical',
5428             15185 => 'Textbooks|Professional & Technical|Design',
5429             15186 => 'Textbooks|Professional & Technical|Language Arts & Disciplines',
5430             15187 => 'Textbooks|Professional & Technical|Engineering',
5431             15188 => 'Textbooks|Professional & Technical|Law',
5432             15189 => 'Textbooks|Professional & Technical|Medical',
5433             15190 => 'Textbooks|Reference',
5434             15191 => 'Textbooks|Reference|Almanacs & Yearbooks',
5435             15192 => 'Textbooks|Reference|Atlases & Maps',
5436             15193 => 'Textbooks|Reference|Catalogs & Directories',
5437             15194 => 'Textbooks|Reference|Consumer Guides',
5438             15195 => 'Textbooks|Reference|Dictionaries & Thesauruses',
5439             15196 => 'Textbooks|Reference|Encyclopedias',
5440             15197 => 'Textbooks|Reference|Etiquette',
5441             15198 => 'Textbooks|Reference|Quotations',
5442             15199 => 'Textbooks|Reference|Study Aids',
5443             15200 => 'Textbooks|Reference|Words & Language',
5444             15201 => 'Textbooks|Reference|Writing',
5445             15202 => 'Textbooks|Religion & Spirituality',
5446             15203 => 'Textbooks|Religion & Spirituality|Bible Studies',
5447             15204 => 'Textbooks|Religion & Spirituality|Bibles',
5448             15205 => 'Textbooks|Religion & Spirituality|Buddhism',
5449             15206 => 'Textbooks|Religion & Spirituality|Christianity',
5450             15207 => 'Textbooks|Religion & Spirituality|Comparative Religion',
5451             15208 => 'Textbooks|Religion & Spirituality|Hinduism',
5452             15209 => 'Textbooks|Religion & Spirituality|Islam',
5453             15210 => 'Textbooks|Religion & Spirituality|Judaism',
5454             15211 => 'Textbooks|Religion & Spirituality|Spirituality',
5455             15212 => 'Textbooks|Romance',
5456             15213 => 'Textbooks|Romance|Contemporary',
5457             15214 => 'Textbooks|Romance|Erotic Romance',
5458             15215 => 'Textbooks|Romance|Paranormal',
5459             15216 => 'Textbooks|Romance|Historical',
5460             15217 => 'Textbooks|Romance|Short Stories',
5461             15218 => 'Textbooks|Romance|Suspense',
5462             15219 => 'Textbooks|Romance|Western',
5463             15220 => 'Textbooks|Sci-Fi & Fantasy',
5464             15221 => 'Textbooks|Sci-Fi & Fantasy|Fantasy',
5465             15222 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Contemporary',
5466             15223 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Epic',
5467             15224 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Historical',
5468             15225 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Paranormal',
5469             15226 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Short Stories',
5470             15227 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction',
5471             15228 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature',
5472             15229 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature|Adventure',
5473             15230 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature|High Tech',
5474             15231 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature|Short Stories',
5475             15232 => 'Textbooks|Science & Nature',
5476             15233 => 'Textbooks|Science & Nature|Agriculture',
5477             15234 => 'Textbooks|Science & Nature|Astronomy',
5478             15235 => 'Textbooks|Science & Nature|Atmosphere',
5479             15236 => 'Textbooks|Science & Nature|Biology',
5480             15237 => 'Textbooks|Science & Nature|Chemistry',
5481             15238 => 'Textbooks|Science & Nature|Earth Sciences',
5482             15239 => 'Textbooks|Science & Nature|Ecology',
5483             15240 => 'Textbooks|Science & Nature|Environment',
5484             15241 => 'Textbooks|Science & Nature|Essays',
5485             15242 => 'Textbooks|Science & Nature|Geography',
5486             15243 => 'Textbooks|Science & Nature|Geology',
5487             15244 => 'Textbooks|Science & Nature|History',
5488             15245 => 'Textbooks|Science & Nature|Life Sciences',
5489             15246 => 'Textbooks|Science & Nature|Nature',
5490             15247 => 'Textbooks|Science & Nature|Physics',
5491             15248 => 'Textbooks|Science & Nature|Reference',
5492             15249 => 'Textbooks|Social Science',
5493             15250 => 'Textbooks|Social Science|Anthropology',
5494             15251 => 'Textbooks|Social Science|Archaeology',
5495             15252 => 'Textbooks|Social Science|Civics',
5496             15253 => 'Textbooks|Social Science|Government',
5497             15254 => 'Textbooks|Social Science|Social Studies',
5498             15255 => 'Textbooks|Social Science|Social Welfare',
5499             15256 => 'Textbooks|Social Science|Society',
5500             15257 => 'Textbooks|Social Science|Society|African Studies',
5501             15258 => 'Textbooks|Social Science|Society|American Studies',
5502             15259 => 'Textbooks|Social Science|Society|Asia Pacific Studies',
5503             15260 => 'Textbooks|Social Science|Society|Cross-Cultural Studies',
5504             15261 => 'Textbooks|Social Science|Society|European Studies',
5505             15262 => 'Textbooks|Social Science|Society|Immigration & Emigration',
5506             15263 => 'Textbooks|Social Science|Society|Indigenous Studies',
5507             15264 => 'Textbooks|Social Science|Society|Latin & Caribbean Studies',
5508             15265 => 'Textbooks|Social Science|Society|Middle Eastern Studies',
5509             15266 => 'Textbooks|Social Science|Society|Race & Ethnicity Studies',
5510             15267 => 'Textbooks|Social Science|Society|Sexuality Studies',
5511             15268 => "Textbooks|Social Science|Society|Women's Studies",
5512             15269 => 'Textbooks|Social Science|Sociology',
5513             15270 => 'Textbooks|Sports & Outdoors',
5514             15271 => 'Textbooks|Sports & Outdoors|Baseball',
5515             15272 => 'Textbooks|Sports & Outdoors|Basketball',
5516             15273 => 'Textbooks|Sports & Outdoors|Coaching',
5517             15274 => 'Textbooks|Sports & Outdoors|Equestrian',
5518             15275 => 'Textbooks|Sports & Outdoors|Extreme Sports',
5519             15276 => 'Textbooks|Sports & Outdoors|Football',
5520             15277 => 'Textbooks|Sports & Outdoors|Golf',
5521             15278 => 'Textbooks|Sports & Outdoors|Hockey',
5522             15279 => 'Textbooks|Sports & Outdoors|Motor Sports',
5523             15280 => 'Textbooks|Sports & Outdoors|Mountaineering',
5524             15281 => 'Textbooks|Sports & Outdoors|Outdoors',
5525             15282 => 'Textbooks|Sports & Outdoors|Racket Sports',
5526             15283 => 'Textbooks|Sports & Outdoors|Reference',
5527             15284 => 'Textbooks|Sports & Outdoors|Soccer',
5528             15285 => 'Textbooks|Sports & Outdoors|Training',
5529             15286 => 'Textbooks|Sports & Outdoors|Water Sports',
5530             15287 => 'Textbooks|Sports & Outdoors|Winter Sports',
5531             15288 => 'Textbooks|Teaching & Learning',
5532             15289 => 'Textbooks|Teaching & Learning|Adult Education',
5533             15290 => 'Textbooks|Teaching & Learning|Curriculum & Teaching',
5534             15291 => 'Textbooks|Teaching & Learning|Educational Leadership',
5535             15292 => 'Textbooks|Teaching & Learning|Educational Technology',
5536             15293 => 'Textbooks|Teaching & Learning|Family & Childcare',
5537             15294 => 'Textbooks|Teaching & Learning|Information & Library Science',
5538             15295 => 'Textbooks|Teaching & Learning|Learning Resources',
5539             15296 => 'Textbooks|Teaching & Learning|Psychology & Research',
5540             15297 => 'Textbooks|Teaching & Learning|Special Education',
5541             15298 => 'Textbooks|Travel & Adventure',
5542             15299 => 'Textbooks|Travel & Adventure|Africa',
5543             15300 => 'Textbooks|Travel & Adventure|Americas',
5544             15301 => 'Textbooks|Travel & Adventure|Americas|Canada',
5545             15302 => 'Textbooks|Travel & Adventure|Americas|Latin America',
5546             15303 => 'Textbooks|Travel & Adventure|Americas|United States',
5547             15304 => 'Textbooks|Travel & Adventure|Asia',
5548             15305 => 'Textbooks|Travel & Adventure|Caribbean',
5549             15306 => 'Textbooks|Travel & Adventure|Essays & Memoirs',
5550             15307 => 'Textbooks|Travel & Adventure|Europe',
5551             15308 => 'Textbooks|Travel & Adventure|Middle East',
5552             15309 => 'Textbooks|Travel & Adventure|Oceania',
5553             15310 => 'Textbooks|Travel & Adventure|Specialty Travel',
5554             15311 => 'Textbooks|Comics & Graphic Novels|Comics',
5555             15312 => 'Textbooks|Reference|Manuals',
5556             16001 => 'App Store|Stickers|Emoji & Expressions',
5557             16003 => 'App Store|Stickers|Animals & Nature',
5558             16005 => 'App Store|Stickers|Art',
5559             16006 => 'App Store|Stickers|Celebrations',
5560             16007 => 'App Store|Stickers|Celebrities',
5561             16008 => 'App Store|Stickers|Comics & Cartoons',
5562             16009 => 'App Store|Stickers|Eating & Drinking',
5563             16010 => 'App Store|Stickers|Gaming',
5564             16014 => 'App Store|Stickers|Movies & TV',
5565             16015 => 'App Store|Stickers|Music',
5566             16017 => 'App Store|Stickers|People',
5567             16019 => 'App Store|Stickers|Places & Objects',
5568             16021 => 'App Store|Stickers|Sports & Activities',
5569             16025 => 'App Store|Stickers|Kids & Family',
5570             16026 => 'App Store|Stickers|Fashion',
5571             100000 => 'Music|Christian & Gospel',
5572             100001 => 'Music|Classical|Art Song',
5573             100002 => 'Music|Classical|Brass & Woodwinds',
5574             100003 => 'Music|Classical|Solo Instrumental',
5575             100004 => 'Music|Classical|Contemporary Era',
5576             100005 => 'Music|Classical|Oratorio',
5577             100006 => 'Music|Classical|Cantata',
5578             100007 => 'Music|Classical|Electronic',
5579             100008 => 'Music|Classical|Sacred',
5580             100009 => 'Music|Classical|Guitar',
5581             100010 => 'Music|Classical|Piano',
5582             100011 => 'Music|Classical|Violin',
5583             100012 => 'Music|Classical|Cello',
5584             100013 => 'Music|Classical|Percussion',
5585             100014 => 'Music|Electronic|Dubstep',
5586             100015 => 'Music|Electronic|Bass',
5587             100016 => 'Music|Hip-Hop/Rap|UK Hip-Hop',
5588             100017 => 'Music|Reggae|Lovers Rock',
5589             100018 => 'Music|Alternative|EMO',
5590             100019 => 'Music|Alternative|Pop Punk',
5591             100020 => 'Music|Alternative|Indie Pop',
5592             100021 => 'Music|New Age|Yoga',
5593             100022 => 'Music|Pop|Tribute',
5594             100023 => 'Music|Pop|Shows',
5595             100024 => 'Music|Cuban',
5596             100025 => 'Music|Cuban|Mambo',
5597             100026 => 'Music|Cuban|Chachacha',
5598             100027 => 'Music|Cuban|Guajira',
5599             100028 => 'Music|Cuban|Son',
5600             100029 => 'Music|Cuban|Bolero',
5601             100030 => 'Music|Cuban|Guaracha',
5602             100031 => 'Music|Cuban|Timba',
5603             100032 => 'Music|Soundtrack|Video Game',
5604             100033 => 'Music|Indian|Regional Indian|Punjabi|Punjabi Pop',
5605             100034 => 'Music|Indian|Regional Indian|Bengali|Rabindra Sangeet',
5606             100035 => 'Music|Indian|Regional Indian|Malayalam',
5607             100036 => 'Music|Indian|Regional Indian|Kannada',
5608             100037 => 'Music|Indian|Regional Indian|Marathi',
5609             100038 => 'Music|Indian|Regional Indian|Gujarati',
5610             100039 => 'Music|Indian|Regional Indian|Assamese',
5611             100040 => 'Music|Indian|Regional Indian|Bhojpuri',
5612             100041 => 'Music|Indian|Regional Indian|Haryanvi',
5613             100042 => 'Music|Indian|Regional Indian|Odia',
5614             100043 => 'Music|Indian|Regional Indian|Rajasthani',
5615             100044 => 'Music|Indian|Regional Indian|Urdu',
5616             100045 => 'Music|Indian|Regional Indian|Punjabi',
5617             100046 => 'Music|Indian|Regional Indian|Bengali',
5618             100047 => 'Music|Indian|Indian Classical|Carnatic Classical',
5619             100048 => 'Music|Indian|Indian Classical|Hindustani Classical',
5620             100049 => 'Music|African|Afro House',
5621             100050 => 'Music|African|Afro Soul',
5622             100051 => 'Music|African|Afrobeats',
5623             100052 => 'Music|African|Benga',
5624             100053 => 'Music|African|Bongo-Flava',
5625             100054 => 'Music|African|Coupe-Decale',
5626             100055 => 'Music|African|Gqom',
5627             100056 => 'Music|African|Highlife',
5628             100057 => 'Music|African|Kuduro',
5629             100058 => 'Music|African|Kizomba',
5630             100059 => 'Music|African|Kwaito',
5631             100060 => 'Music|African|Mbalax',
5632             100061 => 'Music|African|Ndombolo',
5633             100062 => 'Music|African|Shangaan Electro',
5634             100063 => 'Music|African|Soukous',
5635             100064 => 'Music|African|Taarab',
5636             100065 => 'Music|African|Zouglou',
5637             100066 => 'Music|Turkish|Ozgun',
5638             100067 => 'Music|Turkish|Fantezi',
5639             100068 => 'Music|Turkish|Religious',
5640             100069 => 'Music|Pop|Turkish Pop',
5641             100070 => 'Music|Rock|Turkish Rock',
5642             100071 => 'Music|Alternative|Turkish Alternative',
5643             100072 => 'Music|Hip-Hop/Rap|Turkish Hip-Hop/Rap',
5644             100073 => 'Music|African|Maskandi',
5645             100074 => 'Music|Russian|Russian Romance',
5646             100075 => 'Music|Russian|Russian Bard',
5647             100076 => 'Music|Russian|Russian Pop',
5648             100077 => 'Music|Russian|Russian Rock',
5649             100078 => 'Music|Russian|Russian Hip-Hop',
5650             100079 => 'Music|Arabic|Levant',
5651             100080 => 'Music|Arabic|Levant|Dabke',
5652             100081 => 'Music|Arabic|Maghreb Rai',
5653             100082 => 'Music|Arabic|Khaleeji|Khaleeji Jalsat',
5654             100083 => 'Music|Arabic|Khaleeji|Khaleeji Shailat',
5655             100084 => 'Music|Tarab',
5656             100085 => 'Music|Tarab|Iraqi Tarab',
5657             100086 => 'Music|Tarab|Egyptian Tarab',
5658             100087 => 'Music|Tarab|Khaleeji Tarab',
5659             100088 => 'Music|Pop|Levant Pop',
5660             100089 => 'Music|Pop|Iraqi Pop',
5661             100090 => 'Music|Pop|Egyptian Pop',
5662             100091 => 'Music|Pop|Maghreb Pop',
5663             100092 => 'Music|Pop|Khaleeji Pop',
5664             100093 => 'Music|Hip-Hop/Rap|Levant Hip-Hop',
5665             100094 => 'Music|Hip-Hop/Rap|Egyptian Hip-Hop',
5666             100095 => 'Music|Hip-Hop/Rap|Maghreb Hip-Hop',
5667             100096 => 'Music|Hip-Hop/Rap|Khaleeji Hip-Hop',
5668             100097 => 'Music|Alternative|Indie Levant',
5669             100098 => 'Music|Alternative|Indie Egyptian',
5670             100099 => 'Music|Alternative|Indie Maghreb',
5671             100100 => 'Music|Electronic|Levant Electronic',
5672             100101 => "Music|Electronic|Electro-Cha'abi",
5673             100102 => 'Music|Electronic|Maghreb Electronic',
5674             100103 => 'Music|Folk|Iraqi Folk',
5675             100104 => 'Music|Folk|Khaleeji Folk',
5676             100105 => 'Music|Dance|Maghreb Dance',
5677             40000000 => 'iTunes U',
5678             40000001 => 'iTunes U|Business & Economics',
5679             40000002 => 'iTunes U|Business & Economics|Economics',
5680             40000003 => 'iTunes U|Business & Economics|Finance',
5681             40000004 => 'iTunes U|Business & Economics|Hospitality',
5682             40000005 => 'iTunes U|Business & Economics|Management',
5683             40000006 => 'iTunes U|Business & Economics|Marketing',
5684             40000007 => 'iTunes U|Business & Economics|Personal Finance',
5685             40000008 => 'iTunes U|Business & Economics|Real Estate',
5686             40000009 => 'iTunes U|Engineering',
5687             40000010 => 'iTunes U|Engineering|Chemical & Petroleum Engineering',
5688             40000011 => 'iTunes U|Engineering|Civil Engineering',
5689             40000012 => 'iTunes U|Engineering|Computer Science',
5690             40000013 => 'iTunes U|Engineering|Electrical Engineering',
5691             40000014 => 'iTunes U|Engineering|Environmental Engineering',
5692             40000015 => 'iTunes U|Engineering|Mechanical Engineering',
5693             40000016 => 'iTunes U|Music, Art, & Design',
5694             40000017 => 'iTunes U|Music, Art, & Design|Architecture',
5695             40000019 => 'iTunes U|Music, Art, & Design|Art History',
5696             40000020 => 'iTunes U|Music, Art, & Design|Dance',
5697             40000021 => 'iTunes U|Music, Art, & Design|Film',
5698             40000022 => 'iTunes U|Music, Art, & Design|Design',
5699             40000023 => 'iTunes U|Music, Art, & Design|Interior Design',
5700             40000024 => 'iTunes U|Music, Art, & Design|Music',
5701             40000025 => 'iTunes U|Music, Art, & Design|Theater',
5702             40000026 => 'iTunes U|Health & Medicine',
5703             40000027 => 'iTunes U|Health & Medicine|Anatomy & Physiology',
5704             40000028 => 'iTunes U|Health & Medicine|Behavioral Science',
5705             40000029 => 'iTunes U|Health & Medicine|Dentistry',
5706             40000030 => 'iTunes U|Health & Medicine|Diet & Nutrition',
5707             40000031 => 'iTunes U|Health & Medicine|Emergency Medicine',
5708             40000032 => 'iTunes U|Health & Medicine|Genetics',
5709             40000033 => 'iTunes U|Health & Medicine|Gerontology',
5710             40000034 => 'iTunes U|Health & Medicine|Health & Exercise Science',
5711             40000035 => 'iTunes U|Health & Medicine|Immunology',
5712             40000036 => 'iTunes U|Health & Medicine|Neuroscience',
5713             40000037 => 'iTunes U|Health & Medicine|Pharmacology & Toxicology',
5714             40000038 => 'iTunes U|Health & Medicine|Psychiatry',
5715             40000039 => 'iTunes U|Health & Medicine|Global Health',
5716             40000040 => 'iTunes U|Health & Medicine|Radiology',
5717             40000041 => 'iTunes U|History',
5718             40000042 => 'iTunes U|History|Ancient History',
5719             40000043 => 'iTunes U|History|Medieval History',
5720             40000044 => 'iTunes U|History|Military History',
5721             40000045 => 'iTunes U|History|Modern History',
5722             40000046 => 'iTunes U|History|African History',
5723             40000047 => 'iTunes U|History|Asia-Pacific History',
5724             40000048 => 'iTunes U|History|European History',
5725             40000049 => 'iTunes U|History|Middle Eastern History',
5726             40000050 => 'iTunes U|History|North American History',
5727             40000051 => 'iTunes U|History|South American History',
5728             40000053 => 'iTunes U|Communications & Journalism',
5729             40000054 => 'iTunes U|Philosophy',
5730             40000055 => 'iTunes U|Religion & Spirituality',
5731             40000056 => 'iTunes U|Languages',
5732             40000057 => 'iTunes U|Languages|African Languages',
5733             40000058 => 'iTunes U|Languages|Ancient Languages',
5734             40000061 => 'iTunes U|Languages|English',
5735             40000063 => 'iTunes U|Languages|French',
5736             40000064 => 'iTunes U|Languages|German',
5737             40000065 => 'iTunes U|Languages|Italian',
5738             40000066 => 'iTunes U|Languages|Linguistics',
5739             40000068 => 'iTunes U|Languages|Spanish',
5740             40000069 => 'iTunes U|Languages|Speech Pathology',
5741             40000070 => 'iTunes U|Writing & Literature',
5742             40000071 => 'iTunes U|Writing & Literature|Anthologies',
5743             40000072 => 'iTunes U|Writing & Literature|Biography',
5744             40000073 => 'iTunes U|Writing & Literature|Classics',
5745             40000074 => 'iTunes U|Writing & Literature|Literary Criticism',
5746             40000075 => 'iTunes U|Writing & Literature|Fiction',
5747             40000076 => 'iTunes U|Writing & Literature|Poetry',
5748             40000077 => 'iTunes U|Mathematics',
5749             40000078 => 'iTunes U|Mathematics|Advanced Mathematics',
5750             40000079 => 'iTunes U|Mathematics|Algebra',
5751             40000080 => 'iTunes U|Mathematics|Arithmetic',
5752             40000081 => 'iTunes U|Mathematics|Calculus',
5753             40000082 => 'iTunes U|Mathematics|Geometry',
5754             40000083 => 'iTunes U|Mathematics|Statistics',
5755             40000084 => 'iTunes U|Science',
5756             40000085 => 'iTunes U|Science|Agricultural',
5757             40000086 => 'iTunes U|Science|Astronomy',
5758             40000087 => 'iTunes U|Science|Atmosphere',
5759             40000088 => 'iTunes U|Science|Biology',
5760             40000089 => 'iTunes U|Science|Chemistry',
5761             40000090 => 'iTunes U|Science|Ecology',
5762             40000091 => 'iTunes U|Science|Geography',
5763             40000092 => 'iTunes U|Science|Geology',
5764             40000093 => 'iTunes U|Science|Physics',
5765             40000094 => 'iTunes U|Social Science',
5766             40000095 => 'iTunes U|Law & Politics|Law',
5767             40000096 => 'iTunes U|Law & Politics|Political Science',
5768             40000097 => 'iTunes U|Law & Politics|Public Administration',
5769             40000098 => 'iTunes U|Social Science|Psychology',
5770             40000099 => 'iTunes U|Social Science|Social Welfare',
5771             40000100 => 'iTunes U|Social Science|Sociology',
5772             40000101 => 'iTunes U|Society',
5773             40000103 => 'iTunes U|Society|Asia Pacific Studies',
5774             40000104 => 'iTunes U|Society|European Studies',
5775             40000105 => 'iTunes U|Society|Indigenous Studies',
5776             40000106 => 'iTunes U|Society|Latin & Caribbean Studies',
5777             40000107 => 'iTunes U|Society|Middle Eastern Studies',
5778             40000108 => "iTunes U|Society|Women's Studies",
5779             40000109 => 'iTunes U|Teaching & Learning',
5780             40000110 => 'iTunes U|Teaching & Learning|Curriculum & Teaching',
5781             40000111 => 'iTunes U|Teaching & Learning|Educational Leadership',
5782             40000112 => 'iTunes U|Teaching & Learning|Family & Childcare',
5783             40000113 => 'iTunes U|Teaching & Learning|Learning Resources',
5784             40000114 => 'iTunes U|Teaching & Learning|Psychology & Research',
5785             40000115 => 'iTunes U|Teaching & Learning|Special Education',
5786             40000116 => 'iTunes U|Music, Art, & Design|Culinary Arts',
5787             40000117 => 'iTunes U|Music, Art, & Design|Fashion',
5788             40000118 => 'iTunes U|Music, Art, & Design|Media Arts',
5789             40000119 => 'iTunes U|Music, Art, & Design|Photography',
5790             40000120 => 'iTunes U|Music, Art, & Design|Visual Art',
5791             40000121 => 'iTunes U|Business & Economics|Entrepreneurship',
5792             40000122 => 'iTunes U|Communications & Journalism|Broadcasting',
5793             40000123 => 'iTunes U|Communications & Journalism|Digital Media',
5794             40000124 => 'iTunes U|Communications & Journalism|Journalism',
5795             40000125 => 'iTunes U|Communications & Journalism|Photojournalism',
5796             40000126 => 'iTunes U|Communications & Journalism|Print',
5797             40000127 => 'iTunes U|Communications & Journalism|Speech',
5798             40000128 => 'iTunes U|Communications & Journalism|Writing',
5799             40000129 => 'iTunes U|Health & Medicine|Nursing',
5800             40000130 => 'iTunes U|Languages|Arabic',
5801             40000131 => 'iTunes U|Languages|Chinese',
5802             40000132 => 'iTunes U|Languages|Hebrew',
5803             40000133 => 'iTunes U|Languages|Hindi',
5804             40000134 => 'iTunes U|Languages|Indigenous Languages',
5805             40000135 => 'iTunes U|Languages|Japanese',
5806             40000136 => 'iTunes U|Languages|Korean',
5807             40000137 => 'iTunes U|Languages|Other Languages',
5808             40000138 => 'iTunes U|Languages|Portuguese',
5809             40000139 => 'iTunes U|Languages|Russian',
5810             40000140 => 'iTunes U|Law & Politics',
5811             40000141 => 'iTunes U|Law & Politics|Foreign Policy & International Relations',
5812             40000142 => 'iTunes U|Law & Politics|Local Governments',
5813             40000143 => 'iTunes U|Law & Politics|National Governments',
5814             40000144 => 'iTunes U|Law & Politics|World Affairs',
5815             40000145 => 'iTunes U|Writing & Literature|Comparative Literature',
5816             40000146 => 'iTunes U|Philosophy|Aesthetics',
5817             40000147 => 'iTunes U|Philosophy|Epistemology',
5818             40000148 => 'iTunes U|Philosophy|Ethics',
5819             40000149 => 'iTunes U|Philosophy|Metaphysics',
5820             40000150 => 'iTunes U|Philosophy|Political Philosophy',
5821             40000151 => 'iTunes U|Philosophy|Logic',
5822             40000152 => 'iTunes U|Philosophy|Philosophy of Language',
5823             40000153 => 'iTunes U|Philosophy|Philosophy of Religion',
5824             40000154 => 'iTunes U|Social Science|Archaeology',
5825             40000155 => 'iTunes U|Social Science|Anthropology',
5826             40000156 => 'iTunes U|Religion & Spirituality|Buddhism',
5827             40000157 => 'iTunes U|Religion & Spirituality|Christianity',
5828             40000158 => 'iTunes U|Religion & Spirituality|Comparative Religion',
5829             40000159 => 'iTunes U|Religion & Spirituality|Hinduism',
5830             40000160 => 'iTunes U|Religion & Spirituality|Islam',
5831             40000161 => 'iTunes U|Religion & Spirituality|Judaism',
5832             40000162 => 'iTunes U|Religion & Spirituality|Other Religions',
5833             40000163 => 'iTunes U|Religion & Spirituality|Spirituality',
5834             40000164 => 'iTunes U|Science|Environment',
5835             40000165 => 'iTunes U|Society|African Studies',
5836             40000166 => 'iTunes U|Society|American Studies',
5837             40000167 => 'iTunes U|Society|Cross-cultural Studies',
5838             40000168 => 'iTunes U|Society|Immigration & Emigration',
5839             40000169 => 'iTunes U|Society|Race & Ethnicity Studies',
5840             40000170 => 'iTunes U|Society|Sexuality Studies',
5841             40000171 => 'iTunes U|Teaching & Learning|Educational Technology',
5842             40000172 => 'iTunes U|Teaching & Learning|Information/Library Science',
5843             40000173 => 'iTunes U|Languages|Dutch',
5844             40000174 => 'iTunes U|Languages|Luxembourgish',
5845             40000175 => 'iTunes U|Languages|Swedish',
5846             40000176 => 'iTunes U|Languages|Norwegian',
5847             40000177 => 'iTunes U|Languages|Finnish',
5848             40000178 => 'iTunes U|Languages|Danish',
5849             40000179 => 'iTunes U|Languages|Polish',
5850             40000180 => 'iTunes U|Languages|Turkish',
5851             40000181 => 'iTunes U|Languages|Flemish',
5852             50000024 => 'Audiobooks',
5853             50000040 => 'Audiobooks|Fiction',
5854             50000041 => 'Audiobooks|Arts & Entertainment',
5855             50000042 => 'Audiobooks|Biographies & Memoirs',
5856             50000043 => 'Audiobooks|Business & Personal Finance',
5857             50000044 => 'Audiobooks|Kids & Young Adults',
5858             50000045 => 'Audiobooks|Classics',
5859             50000046 => 'Audiobooks|Comedy',
5860             50000047 => 'Audiobooks|Drama & Poetry',
5861             50000048 => 'Audiobooks|Speakers & Storytellers',
5862             50000049 => 'Audiobooks|History',
5863             50000050 => 'Audiobooks|Languages',
5864             50000051 => 'Audiobooks|Mysteries & Thrillers',
5865             50000052 => 'Audiobooks|Nonfiction',
5866             50000053 => 'Audiobooks|Religion & Spirituality',
5867             50000054 => 'Audiobooks|Science & Nature',
5868             50000055 => 'Audiobooks|Sci Fi & Fantasy',
5869             50000056 => 'Audiobooks|Self-Development',
5870             50000057 => 'Audiobooks|Sports & Outdoors',
5871             50000058 => 'Audiobooks|Technology',
5872             50000059 => 'Audiobooks|Travel & Adventure',
5873             50000061 => 'Music|Spoken Word',
5874             50000063 => 'Music|Disney',
5875             50000064 => 'Music|French Pop',
5876             50000066 => 'Music|German Pop',
5877             50000068 => 'Music|German Folk',
5878             50000069 => 'Audiobooks|Romance',
5879             50000070 => 'Audiobooks|Audiobooks Latino',
5880             50000071 => 'Books|Comics & Graphic Novels|Manga|Action',
5881             50000072 => 'Books|Comics & Graphic Novels|Manga|Comedy',
5882             50000073 => 'Books|Comics & Graphic Novels|Manga|Erotica',
5883             50000074 => 'Books|Comics & Graphic Novels|Manga|Fantasy',
5884             50000075 => 'Books|Comics & Graphic Novels|Manga|Four Cell Manga',
5885             50000076 => 'Books|Comics & Graphic Novels|Manga|Gay & Lesbian',
5886             50000077 => 'Books|Comics & Graphic Novels|Manga|Hard-Boiled',
5887             50000078 => 'Books|Comics & Graphic Novels|Manga|Heroes',
5888             50000079 => 'Books|Comics & Graphic Novels|Manga|Historical Fiction',
5889             50000080 => 'Books|Comics & Graphic Novels|Manga|Mecha',
5890             50000081 => 'Books|Comics & Graphic Novels|Manga|Mystery',
5891             50000082 => 'Books|Comics & Graphic Novels|Manga|Nonfiction',
5892             50000083 => 'Books|Comics & Graphic Novels|Manga|Religious',
5893             50000084 => 'Books|Comics & Graphic Novels|Manga|Romance',
5894             50000085 => 'Books|Comics & Graphic Novels|Manga|Romantic Comedy',
5895             50000086 => 'Books|Comics & Graphic Novels|Manga|Science Fiction',
5896             50000087 => 'Books|Comics & Graphic Novels|Manga|Sports',
5897             50000088 => 'Books|Fiction & Literature|Light Novels',
5898             50000089 => 'Books|Comics & Graphic Novels|Manga|Horror',
5899             50000090 => 'Books|Comics & Graphic Novels|Comics',
5900             50000091 => 'Books|Romance|Multicultural',
5901             50000092 => 'Audiobooks|Erotica',
5902             50000093 => 'Audiobooks|Light Novels',
5903             },
5904             },
5905             grup => { Name => 'Grouping', Avoid => 1 }, #10
5906             hdvd => { #10
5907             Name => 'HDVideo',
5908             Format => 'int8u', #24
5909             Writable => 'int8s', #27
5910             PrintConv => { 0 => 'No', 1 => 'Yes' },
5911             },
5912             keyw => 'Keyword', #7
5913             ldes => 'LongDescription', #10
5914             pcst => { #7
5915             Name => 'Podcast',
5916             Format => 'int8u', #23
5917             Writable => 'int8s', #27
5918             PrintConv => { 0 => 'No', 1 => 'Yes' },
5919             },
5920             perf => 'Performer',
5921             plID => { #10 (or TV season)
5922             Name => 'PlayListID',
5923             Format => 'int8u', # actually int64u, but split it up
5924             Writable => 'int32s', #27
5925             },
5926             purd => 'PurchaseDate', #7
5927             purl => 'PodcastURL', #7
5928             rtng => { #10
5929             Name => 'Rating',
5930             Format => 'int8u', #23
5931             Writable => 'int8s', #27
5932             PrintConv => {
5933             0 => 'none',
5934             1 => 'Explicit',
5935             2 => 'Clean',
5936             4 => 'Explicit (old)',
5937             },
5938             },
5939             sfID => { #10
5940             Name => 'AppleStoreCountry',
5941             Format => 'int32u',
5942             Writable => 'int32s', #27
5943             SeparateTable => 1,
5944             PrintConv => { #21
5945             143441 => 'United States', # US
5946             143442 => 'France', # FR
5947             143443 => 'Germany', # DE
5948             143444 => 'United Kingdom', # GB
5949             143445 => 'Austria', # AT
5950             143446 => 'Belgium', # BE
5951             143447 => 'Finland', # FI
5952             143448 => 'Greece', # GR
5953             143449 => 'Ireland', # IE
5954             143450 => 'Italy', # IT
5955             143451 => 'Luxembourg', # LU
5956             143452 => 'Netherlands', # NL
5957             143453 => 'Portugal', # PT
5958             143454 => 'Spain', # ES
5959             143455 => 'Canada', # CA
5960             143456 => 'Sweden', # SE
5961             143457 => 'Norway', # NO
5962             143458 => 'Denmark', # DK
5963             143459 => 'Switzerland', # CH
5964             143460 => 'Australia', # AU
5965             143461 => 'New Zealand', # NZ
5966             143462 => 'Japan', # JP
5967             143463 => 'Hong Kong', # HK
5968             143464 => 'Singapore', # SG
5969             143465 => 'China', # CN
5970             143466 => 'Republic of Korea', # KR
5971             143467 => 'India', # IN
5972             143468 => 'Mexico', # MX
5973             143469 => 'Russia', # RU
5974             143470 => 'Taiwan', # TW
5975             143471 => 'Vietnam', # VN
5976             143472 => 'South Africa', # ZA
5977             143473 => 'Malaysia', # MY
5978             143474 => 'Philippines', # PH
5979             143475 => 'Thailand', # TH
5980             143476 => 'Indonesia', # ID
5981             143477 => 'Pakistan', # PK
5982             143478 => 'Poland', # PL
5983             143479 => 'Saudi Arabia', # SA
5984             143480 => 'Turkey', # TR
5985             143481 => 'United Arab Emirates', # AE
5986             143482 => 'Hungary', # HU
5987             143483 => 'Chile', # CL
5988             143484 => 'Nepal', # NP
5989             143485 => 'Panama', # PA
5990             143486 => 'Sri Lanka', # LK
5991             143487 => 'Romania', # RO
5992             143489 => 'Czech Republic', # CZ
5993             143491 => 'Israel', # IL
5994             143492 => 'Ukraine', # UA
5995             143493 => 'Kuwait', # KW
5996             143494 => 'Croatia', # HR
5997             143495 => 'Costa Rica', # CR
5998             143496 => 'Slovakia', # SK
5999             143497 => 'Lebanon', # LB
6000             143498 => 'Qatar', # QA
6001             143499 => 'Slovenia', # SI
6002             143501 => 'Colombia', # CO
6003             143502 => 'Venezuela', # VE
6004             143503 => 'Brazil', # BR
6005             143504 => 'Guatemala', # GT
6006             143505 => 'Argentina', # AR
6007             143506 => 'El Salvador', # SV
6008             143507 => 'Peru', # PE
6009             143508 => 'Dominican Republic', # DO
6010             143509 => 'Ecuador', # EC
6011             143510 => 'Honduras', # HN
6012             143511 => 'Jamaica', # JM
6013             143512 => 'Nicaragua', # NI
6014             143513 => 'Paraguay', # PY
6015             143514 => 'Uruguay', # UY
6016             143515 => 'Macau', # MO
6017             143516 => 'Egypt', # EG
6018             143517 => 'Kazakhstan', # KZ
6019             143518 => 'Estonia', # EE
6020             143519 => 'Latvia', # LV
6021             143520 => 'Lithuania', # LT
6022             143521 => 'Malta', # MT
6023             143523 => 'Moldova', # MD
6024             143524 => 'Armenia', # AM
6025             143525 => 'Botswana', # BW
6026             143526 => 'Bulgaria', # BG
6027             143528 => 'Jordan', # JO
6028             143529 => 'Kenya', # KE
6029             143530 => 'Macedonia', # MK
6030             143531 => 'Madagascar', # MG
6031             143532 => 'Mali', # ML
6032             143533 => 'Mauritius', # MU
6033             143534 => 'Niger', # NE
6034             143535 => 'Senegal', # SN
6035             143536 => 'Tunisia', # TN
6036             143537 => 'Uganda', # UG
6037             143538 => 'Anguilla', # AI
6038             143539 => 'Bahamas', # BS
6039             143540 => 'Antigua and Barbuda', # AG
6040             143541 => 'Barbados', # BB
6041             143542 => 'Bermuda', # BM
6042             143543 => 'British Virgin Islands', # VG
6043             143544 => 'Cayman Islands', # KY
6044             143545 => 'Dominica', # DM
6045             143546 => 'Grenada', # GD
6046             143547 => 'Montserrat', # MS
6047             143548 => 'St. Kitts and Nevis', # KN
6048             143549 => 'St. Lucia', # LC
6049             143550 => 'St. Vincent and The Grenadines', # VC
6050             143551 => 'Trinidad and Tobago', # TT
6051             143552 => 'Turks and Caicos', # TC
6052             143553 => 'Guyana', # GY
6053             143554 => 'Suriname', # SR
6054             143555 => 'Belize', # BZ
6055             143556 => 'Bolivia', # BO
6056             143557 => 'Cyprus', # CY
6057             143558 => 'Iceland', # IS
6058             143559 => 'Bahrain', # BH
6059             143560 => 'Brunei Darussalam', # BN
6060             143561 => 'Nigeria', # NG
6061             143562 => 'Oman', # OM
6062             143563 => 'Algeria', # DZ
6063             143564 => 'Angola', # AO
6064             143565 => 'Belarus', # BY
6065             143566 => 'Uzbekistan', # UZ
6066             143568 => 'Azerbaijan', # AZ
6067             143571 => 'Yemen', # YE
6068             143572 => 'Tanzania', # TZ
6069             143573 => 'Ghana', # GH
6070             143575 => 'Albania', # AL
6071             143576 => 'Benin', # BJ
6072             143577 => 'Bhutan', # BT
6073             143578 => 'Burkina Faso', # BF
6074             143579 => 'Cambodia', # KH
6075             143580 => 'Cape Verde', # CV
6076             143581 => 'Chad', # TD
6077             143582 => 'Republic of the Congo', # CG
6078             143583 => 'Fiji', # FJ
6079             143584 => 'Gambia', # GM
6080             143585 => 'Guinea-Bissau', # GW
6081             143586 => 'Kyrgyzstan', # KG
6082             143587 => "Lao People's Democratic Republic", # LA
6083             143588 => 'Liberia', # LR
6084             143589 => 'Malawi', # MW
6085             143590 => 'Mauritania', # MR
6086             143591 => 'Federated States of Micronesia', # FM
6087             143592 => 'Mongolia', # MN
6088             143593 => 'Mozambique', # MZ
6089             143594 => 'Namibia', # NA
6090             143595 => 'Palau', # PW
6091             143597 => 'Papua New Guinea', # PG
6092             143598 => 'Sao Tome and Principe', # ST (São Tomé and Príncipe)
6093             143599 => 'Seychelles', # SC
6094             143600 => 'Sierra Leone', # SL
6095             143601 => 'Solomon Islands', # SB
6096             143602 => 'Swaziland', # SZ
6097             143603 => 'Tajikistan', # TJ
6098             143604 => 'Turkmenistan', # TM
6099             143605 => 'Zimbabwe', # ZW
6100             },
6101             },
6102             soaa => 'SortAlbumArtist', #10
6103             soal => 'SortAlbum', #10
6104             soar => 'SortArtist', #10
6105             soco => 'SortComposer', #10
6106             sonm => 'SortName', #10
6107             sosn => 'SortShow', #10
6108             stik => { #10
6109             Name => 'MediaType',
6110             Format => 'int8u', #23
6111             Writable => 'int8s', #27
6112             PrintConvColumns => 2,
6113             PrintConv => { #(http://weblog.xanga.com/gryphondwb/615474010/iphone-ringtones---what-did-itunes-741-really-do.html)
6114             0 => 'Movie (old)', #forum9059 (was Movie)
6115             1 => 'Normal (Music)',
6116             2 => 'Audiobook',
6117             5 => 'Whacked Bookmark',
6118             6 => 'Music Video',
6119             9 => 'Movie', #forum9059 (was Short Film)
6120             10 => 'TV Show',
6121             11 => 'Booklet',
6122             14 => 'Ringtone',
6123             21 => 'Podcast', #15
6124             23 => 'iTunes U', #forum9059
6125             },
6126             },
6127             rate => 'RatingPercent', #PH
6128             titl => { Name => 'Title', Avoid => 1 },
6129             tven => 'TVEpisodeID', #7
6130             tves => { #7/10
6131             Name => 'TVEpisode',
6132             Format => 'int32u',
6133             Writable => 'int32s', #27
6134             },
6135             tvnn => 'TVNetworkName', #7
6136             tvsh => 'TVShow', #10
6137             tvsn => { #7/10
6138             Name => 'TVSeason',
6139             Format => 'int32u',
6140             },
6141             yrrc => 'Year', #(ffmpeg source)
6142             itnu => { #PH (iTunes 10.5)
6143             Name => 'iTunesU',
6144             Format => 'int8u', #27
6145             Writable => 'int8s', #27
6146             Description => 'iTunes U',
6147             PrintConv => { 0 => 'No', 1 => 'Yes' },
6148             },
6149             #https://github.com/communitymedia/mediautilities/blob/master/src/net/sourceforge/jaad/mp4/boxes/BoxTypes.java
6150             gshh => { Name => 'GoogleHostHeader', Format => 'string' },
6151             gspm => { Name => 'GooglePingMessage', Format => 'string' },
6152             gspu => { Name => 'GooglePingURL', Format => 'string' },
6153             gssd => { Name => 'GoogleSourceData', Format => 'string' },
6154             gsst => { Name => 'GoogleStartTime', Format => 'string' },
6155             gstd => {
6156             Name => 'GoogleTrackDuration',
6157             Format => 'string',
6158             ValueConv => '$val / 1000',
6159             ValueConvInv => '$val * 1000',
6160             PrintConv => 'ConvertDuration($val)',
6161             PrintConvInv => q{
6162             $val =~ s/ s$//;
6163             my @a = split /(:| days )/, $val;
6164             my $sign = ($val =~ s/^-//) ? -1 : 1;
6165             $a[0] += shift(@a) * 24 if @a == 4;
6166             $a[0] += shift(@a) * 60 while @a > 1;
6167             return $a[0] * $sign;
6168             },
6169             },
6170              
6171             # atoms observed in AAX audiobooks (ref PH)
6172             "\xa9cpy" => { Name => 'Copyright', Avoid => 1, Groups => { 2 => 'Author' } },
6173             "\xa9pub" => 'Publisher',
6174             "\xa9nrt" => 'Narrator',
6175             '@pti' => 'ParentTitle', # (guess -- same as "\xa9nam")
6176             '@PST' => 'ParentShortTitle', # (guess -- same as "\xa9nam")
6177             '@ppi' => 'ParentProductID', # (guess -- same as 'prID')
6178             '@sti' => 'ShortTitle', # (guess -- same as "\xa9nam")
6179             prID => 'ProductID',
6180             rldt => { Name => 'ReleaseDate', Groups => { 2 => 'Time' }},
6181             CDEK => { Name => 'Unknown_CDEK', Unknown => 1 }, # eg: "B004ZMTFEG" - used in URL's ("asin=")
6182             CDET => { Name => 'Unknown_CDET', Unknown => 1 }, # eg: "ADBL"
6183             VERS => 'ProductVersion',
6184             GUID => 'GUID',
6185             AACR => { Name => 'Unknown_AACR', Unknown => 1 }, # eg: "CR!1T1H1QH6WX7T714G2BMFX3E9MC4S"
6186             # ausr - 30 bytes (User Alias?)
6187             "\xa9xyz" => { #PH (written by Google Photos)
6188             Name => 'GPSCoordinates',
6189             Groups => { 2 => 'Location' },
6190             ValueConv => \&ConvertISO6709,
6191             ValueConvInv => \&ConvInvISO6709,
6192             PrintConv => \&PrintGPSCoordinates,
6193             PrintConvInv => \&PrintInvGPSCoordinates,
6194             },
6195             # the following tags written by iTunes 12.5.1.21
6196             # (ref https://www.ventismedia.com/mantis/view.php?id=14963
6197             # https://community.mp3tag.de/t/x-mp4-new-tag-problems/19488)
6198             "\xa9wrk" => 'Work', #PH
6199             "\xa9mvn" => 'MovementName', #PH
6200             "\xa9mvi" => { #PH
6201             Name => 'MovementNumber',
6202             Format => 'int16u', #27
6203             Writable => 'int16s', #27
6204             },
6205             "\xa9mvc" => { #PH
6206             Name => 'MovementCount',
6207             Format => 'int16u', #27
6208             Writable => 'int16s', #27
6209             },
6210             shwm => { #PH
6211             Name => 'ShowMovement',
6212             Format => 'int8u', #27
6213             Writable => 'int8s', #27
6214             PrintConv => { 0 => 'No', 1 => 'Yes' },
6215             },
6216             );
6217              
6218             # tag decoded from timed face records
6219             %Image::ExifTool::QuickTime::FaceInfo = (
6220             PROCESS_PROC => \&ProcessMOV,
6221             GROUPS => { 2 => 'Video' },
6222             crec => {
6223             Name => 'FaceRec',
6224             SubDirectory => {
6225             TagTable => 'Image::ExifTool::QuickTime::FaceRec',
6226             },
6227             },
6228             );
6229              
6230             # tag decoded from timed face records
6231             %Image::ExifTool::QuickTime::FaceRec = (
6232             PROCESS_PROC => \&ProcessMOV,
6233             GROUPS => { 2 => 'Video' },
6234             cits => {
6235             Name => 'FaceItem',
6236             SubDirectory => {
6237             TagTable => 'Image::ExifTool::QuickTime::Keys',
6238             ProcessProc => \&Process_mebx,
6239             },
6240             },
6241             );
6242              
6243             # item list keys (ref PH)
6244             %Image::ExifTool::QuickTime::Keys = (
6245             PROCESS_PROC => \&ProcessKeys,
6246             WRITE_PROC => \&WriteKeys,
6247             CHECK_PROC => \&CheckQTValue,
6248             VARS => { LONG_TAGS => 7 },
6249             WRITABLE => 1,
6250             # (not PREFERRED when writing)
6251             GROUPS => { 1 => 'Keys' },
6252             WRITE_GROUP => 'Keys',
6253             LANG_INFO => \&GetLangInfo,
6254             NOTES => q{
6255             This directory contains a list of key names which are used to decode tags
6256             written by the "mdta" handler. Also in this table are a few tags found in
6257             timed metadata that are not yet writable by ExifTool. The prefix of
6258             "com.apple.quicktime." has been removed from the TagID's below. These tags
6259             support alternate languages in the same way as the
6260             L tags. Note
6261             that by default,
6262             L and
6263             L tags are
6264             preferred when writing, so to create a tag when a same-named tag exists in
6265             either of these tables, either the "Keys" location must be specified (eg.
6266             C<-Keys:Author=Phil> on the command line), or the PREFERRED level must be
6267             changed via L.
6268             },
6269             version => 'Version',
6270             album => 'Album',
6271             artist => { },
6272             artwork => { },
6273             author => { Name => 'Author', Groups => { 2 => 'Author' } },
6274             comment => { },
6275             copyright => { Name => 'Copyright', Groups => { 2 => 'Author' } },
6276             creationdate=> {
6277             Name => 'CreationDate',
6278             Groups => { 2 => 'Time' },
6279             Shift => 'Time',
6280             ValueConv => q{
6281             require Image::ExifTool::XMP;
6282             $val = Image::ExifTool::XMP::ConvertXMPDate($val,1);
6283             $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
6284             return $val;
6285             },
6286             ValueConvInv => q{
6287             require Image::ExifTool::XMP;
6288             $val = Image::ExifTool::XMP::FormatXMPDate($val);
6289             $val =~ s/([-+]\d{2}):(\d{2})$/$1$2/; # remove time zone colon
6290             return $val;
6291             },
6292             PrintConv => '$self->ConvertDateTime($val)',
6293             PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
6294             },
6295             description => { },
6296             director => { },
6297             displayname => { Name => 'DisplayName' },
6298             title => { }, #22
6299             genre => { },
6300             information => { },
6301             keywords => { },
6302             producer => { }, #22
6303             make => { Name => 'Make', Groups => { 2 => 'Camera' } },
6304             model => { Name => 'Model', Groups => { 2 => 'Camera' } },
6305             publisher => { },
6306             software => { },
6307             year => { Groups => { 2 => 'Time' } },
6308             'camera.identifier' => 'CameraIdentifier', # (iPhone 4)
6309             'camera.framereadouttimeinmicroseconds' => { # (iPhone 4)
6310             Name => 'FrameReadoutTime',
6311             ValueConv => '$val * 1e-6',
6312             ValueConvInv => 'int($val * 1e6 + 0.5)',
6313             PrintConv => '$val * 1e6 . " microseconds"',
6314             PrintConvInv => '$val =~ s/ .*//; $val * 1e-6',
6315             },
6316             'location.ISO6709' => {
6317             Name => 'GPSCoordinates',
6318             Groups => { 2 => 'Location' },
6319             Notes => q{
6320             Google Photos may ignore this if the coorinates have more than 5 digits
6321             after the decimal
6322             },
6323             ValueConv => \&ConvertISO6709,
6324             ValueConvInv => \&ConvInvISO6709,
6325             PrintConv => \&PrintGPSCoordinates,
6326             PrintConvInv => \&PrintInvGPSCoordinates,
6327             },
6328             'location.name' => { Name => 'LocationName', Groups => { 2 => 'Location' } },
6329             'location.body' => { Name => 'LocationBody', Groups => { 2 => 'Location' } },
6330             'location.note' => { Name => 'LocationNote', Groups => { 2 => 'Location' } },
6331             'location.role' => {
6332             Name => 'LocationRole',
6333             Groups => { 2 => 'Location' },
6334             PrintConv => {
6335             0 => 'Shooting Location',
6336             1 => 'Real Location',
6337             2 => 'Fictional Location',
6338             },
6339             },
6340             'location.date' => {
6341             Name => 'LocationDate',
6342             Groups => { 2 => 'Time' },
6343             Shift => 'Time',
6344             ValueConv => q{
6345             require Image::ExifTool::XMP;
6346             $val = Image::ExifTool::XMP::ConvertXMPDate($val);
6347             $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
6348             return $val;
6349             },
6350             ValueConvInv => q{
6351             require Image::ExifTool::XMP;
6352             $val = Image::ExifTool::XMP::FormatXMPDate($val);
6353             $val =~ s/([-+]\d{2}):(\d{2})$/$1$2/; # remove time zone colon
6354             return $val;
6355             },
6356             PrintConv => '$self->ConvertDateTime($val)',
6357             PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
6358             },
6359             'location.accuracy.horizontal' => { Name => 'LocationAccuracyHorizontal' },
6360             'live-photo.auto' => { Name => 'LivePhotoAuto', Writable => 'int8u' },
6361             'live-photo.vitality-score' => { Name => 'LivePhotoVitalityScore', Writable => 'float' },
6362             'live-photo.vitality-scoring-version' => { Name => 'LivePhotoVitalityScoringVersion', Writable => 'int64s' },
6363             'apple.photos.variation-identifier' => { Name => 'ApplePhotosVariationIdentifier', Writable => 'int64s' },
6364             'direction.facing' => { Name => 'CameraDirection', Groups => { 2 => 'Location' } },
6365             'direction.motion' => { Name => 'CameraMotion', Groups => { 2 => 'Location' } },
6366             'location.body' => { Name => 'LocationBody', Groups => { 2 => 'Location' } },
6367             'player.version' => 'PlayerVersion',
6368             'player.movie.visual.brightness'=> 'Brightness',
6369             'player.movie.visual.color' => 'Color',
6370             'player.movie.visual.tint' => 'Tint',
6371             'player.movie.visual.contrast' => 'Contrast',
6372             'player.movie.audio.gain' => 'AudioGain',
6373             'player.movie.audio.treble' => 'Treble',
6374             'player.movie.audio.bass' => 'Bass',
6375             'player.movie.audio.balance' => 'Balance',
6376             'player.movie.audio.pitchshift' => 'PitchShift',
6377             'player.movie.audio.mute' => {
6378             Name => 'Mute',
6379             Format => 'int8u',
6380             PrintConv => { 0 => 'Off', 1 => 'On' },
6381             },
6382             'rating.user' => 'UserRating', # (Canon ELPH 510 HS)
6383             'collection.user' => 'UserCollection', #22
6384             'Encoded_With' => 'EncodedWith',
6385             #
6386             # the following tags aren't in the com.apple.quicktime namespace:
6387             #
6388             'com.apple.photos.captureMode' => 'CaptureMode',
6389             'com.android.version' => 'AndroidVersion',
6390             #
6391             # also seen
6392             #
6393             # com.divergentmedia.clipwrap.model ('NEX-FS700EK')
6394             # com.divergentmedia.clipwrap.model1 ('49')
6395             # com.divergentmedia.clipwrap.model2 ('0')
6396             # com.divergentmedia.clipwrap.manufacturer ('Sony')
6397             # com.divergentmedia.clipwrap.originalDateTime ('2013/2/6 10:30:40+0200')
6398             #
6399             # seen in timed metadata (mebx), and added dynamically to the table via SaveMetaKeys()
6400             # NOTE: these tags are not writable! (timed metadata cannot yet be written)
6401             #
6402             # (mdta)com.apple.quicktime.video-orientation (dtyp=66, int16s)
6403             'video-orientation' => {
6404             Name => 'VideoOrientation',
6405             Writable => 0,
6406             PrintConv => \%Image::ExifTool::Exif::orientation, #PH (NC)
6407             },
6408             # (mdta)com.apple.quicktime.live-photo-info (dtyp=com.apple.quicktime.com.apple.quicktime.live-photo-info)
6409             'live-photo-info' => {
6410             Name => 'LivePhotoInfo',
6411             Writable => 0,
6412             # not sure what these values mean, but unpack them anyway - PH
6413             # (ignore the fact that the "f" and "l" unpacks won't work on a big-endian machine)
6414             ValueConv => 'join " ",unpack "VfVVf6c4lCCcclf4Vvv", $val',
6415             },
6416             # (mdta)com.apple.quicktime.still-image-time (dtyp=65, int8s)
6417             'still-image-time' => { # (found in live photo)
6418             Name => 'StillImageTime',
6419             Writable => 0,
6420             Notes => q{
6421             this tag always has a value of -1; the time of the still image is obtained
6422             from the associated SampleTime
6423             },
6424             },
6425             # (mdta)com.apple.quicktime.detected-face (dtyp='com.apple.quicktime.detected-face')
6426             'detected-face' => {
6427             Name => 'FaceInfo',
6428             Writable => 0,
6429             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::FaceInfo' },
6430             },
6431             # ---- detected-face fields ( ----
6432             # --> back here after a round trip through FaceInfo -> FaceRec -> FaceItem
6433             # (fiel)com.apple.quicktime.detected-face.bounds (dtyp=80, float[8])
6434             'detected-face.bounds' => {
6435             Name => 'DetectedFaceBounds',
6436             Writable => 0,
6437             # round to a reasonable number of decimal places
6438             PrintConv => 'my @a=split " ",$val;$_=int($_*1e6+.5)/1e6 foreach @a;join " ",@a',
6439             PrintConvInv => '$val',
6440             },
6441             # (fiel)com.apple.quicktime.detected-face.face-id (dtyp=77, int32u)
6442             'detected-face.face-id' => { Name => 'DetectedFaceID', Writable => 0 },
6443             # (fiel)com.apple.quicktime.detected-face.roll-angle (dtyp=23, float)
6444             'detected-face.roll-angle' => { Name => 'DetectedFaceRollAngle', Writable => 0 },
6445             # (fiel)com.apple.quicktime.detected-face.yaw-angle (dtyp=23, float)
6446             'detected-face.yaw-angle' => { Name => 'DetectedFaceYawAngle', Writable => 0 },
6447             #
6448             # seen in Apple ProRes RAW file
6449             #
6450             # (mdta)com.apple.proapps.manufacturer (eg. "Sony")
6451             # (mdta)com.apple.proapps.exif.{Exif}.FNumber (float, eg. 1.0)
6452             # (mdta)org.smpte.rdd18.lens.irisfnumber (eg. "F1.0")
6453             # (mdta)com.apple.proapps.exif.{Exif}.ShutterSpeedValue (float, eg. 1.006)
6454             # (mdta)org.smpte.rdd18.camera.shutterspeed_angle (eg. "179.2deg")
6455             # (mdta)org.smpte.rdd18.camera.neutraldensityfilterwheelsetting (eg. "ND1")
6456             # (mdta)org.smpte.rdd18.camera.whitebalance (eg. "4300K")
6457             # (mdta)com.apple.proapps.exif.{Exif}.ExposureIndex (float, eg. 4000)
6458             # (mdta)org.smpte.rdd18.camera.isosensitivity (eg. "4000")
6459             # (mdta)com.apple.proapps.image.{TIFF}.Make (eg. "Atmos")
6460             # (mdta)com.apple.proapps.image.{TIFF}.Model (eg. "ShogunInferno")
6461             # (mdta)com.apple.proapps.image.{TIFF}.Software (eg. "9.0")
6462             );
6463              
6464             # iTunes info ('----') atoms
6465             %Image::ExifTool::QuickTime::iTunesInfo = (
6466             PROCESS_PROC => \&ProcessMOV,
6467             GROUPS => { 1 => 'iTunes', 2 => 'Audio' },
6468             VARS => { LONG_TAGS => 0 }, # (hack for discrepancy in the way long tags are counted in BuildTagLookup)
6469             NOTES => q{
6470             ExifTool will extract any iTunesInfo tags that exist, even if they are not
6471             defined in this table. These tags belong to the family 1 "iTunes" group,
6472             and are not currently writable.
6473             },
6474             # 'mean'/'name'/'data' atoms form a triplet, but unfortunately
6475             # I haven't been able to find any documentation on this.
6476             # 'mean' is normally 'com.apple.iTunes'
6477             mean => {
6478             Name => 'Mean',
6479             # the 'Triplet' flag tells ProcessMOV() to generate
6480             # a single tag from the mean/name/data triplet
6481             Triplet => 1,
6482             Hidden => 1,
6483             },
6484             name => {
6485             Name => 'Name',
6486             Triplet => 1,
6487             Hidden => 1,
6488             },
6489             data => {
6490             Name => 'Data',
6491             Triplet => 1,
6492             Hidden => 1,
6493             },
6494             # the tag ID's below are composed from "mean/name",
6495             # but "mean/" is omitted if it is "com.apple.iTunes/":
6496             'iTunMOVI' => {
6497             Name => 'iTunMOVI',
6498             SubDirectory => { TagTable => 'Image::ExifTool::PLIST::Main' },
6499             },
6500             'tool' => {
6501             Name => 'iTunTool',
6502             Description => 'iTunTool',
6503             Format => 'int32u',
6504             PrintConv => 'sprintf("0x%.8x",$val)',
6505             },
6506             'iTunEXTC' => {
6507             Name => 'ContentRating',
6508             Notes => 'standard | rating | score | reasons',
6509             # eg. 'us-tv|TV-14|500|V', 'mpaa|PG-13|300|For violence and sexuality'
6510             # (see http://shadowofged.blogspot.ca/2008/06/itunes-content-ratings.html)
6511             },
6512             'iTunNORM' => {
6513             Name => 'VolumeNormalization',
6514             PrintConv => '$val=~s/ 0+(\w)/ $1/g; $val=~s/^\s+//; $val',
6515             },
6516             'iTunSMPB' => {
6517             Name => 'iTunSMPB',
6518             Description => 'iTunSMPB',
6519             # hex format, similar to iTunNORM, but 12 words instead of 10,
6520             # and 4th word is 16 hex digits (all others are 8)
6521             # (gives AAC encoder delay, ref http://code.google.com/p/l-smash/issues/detail?id=1)
6522             PrintConv => '$val=~s/ 0+(\w)/ $1/g; $val=~s/^\s+//; $val',
6523             },
6524             # (CDDB = Compact Disc DataBase)
6525             # iTunes_CDDB_1 = +<# tracks>+...
6526             'iTunes_CDDB_1' => 'CDDB1Info',
6527             'iTunes_CDDB_TrackNumber' => 'CDDBTrackNumber',
6528             'Encoding Params' => {
6529             Name => 'EncodingParams',
6530             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::EncodingParams' },
6531             },
6532             # also heard about 'iTunPGAP', but I haven't seen a sample
6533             # all tags below were added based on samples I have seen - PH
6534             DISCNUMBER => 'DiscNumber',
6535             TRACKNUMBER => 'TrackNumber',
6536             ARTISTS => 'Artists',
6537             CATALOGNUMBER => 'CatalogNumber',
6538             RATING => 'Rating',
6539             MEDIA => 'Media',
6540             SCRIPT => 'Script', # character set? (seen 'Latn')
6541             BARCODE => 'Barcode',
6542             LABEL => 'Label',
6543             MOOD => 'Mood',
6544             popularimeter => 'Popularimeter',
6545             'Dynamic Range (DR)'=> 'DynamicRange',
6546             initialkey => 'InitialKey',
6547             originalyear => 'OriginalYear',
6548             originaldate => 'OriginalDate',
6549             '~length' => 'Length', # play length? (ie. duration?)
6550             replaygain_track_gain=>'ReplayTrackGain',
6551             replaygain_track_peak=>'ReplayTrackPeak',
6552             'Volume Level (ReplayGain)'=> 'ReplayVolumeLevel',
6553             'Dynamic Range (R128)'=> 'DynamicRangeR128',
6554             'Volume Level (R128)' => 'VolumeLevelR128',
6555             'Peak Level (Sample)' => 'PeakLevelSample',
6556             'Peak Level (R128)' => 'PeakLevelR128',
6557             # also seen (many from forum12777):
6558             # 'MusicBrainz Album Release Country'
6559             # 'MusicBrainz Album Type'
6560             # 'MusicBrainz Album Status'
6561             # 'MusicBrainz Track Id'
6562             # 'MusicBrainz Release Track Id'
6563             # 'MusicBrainz Album Id'
6564             # 'MusicBrainz Album Artist Id'
6565             # 'MusicBrainz Artist Id'
6566             # 'Acoustid Id' (sic)
6567             # 'Tool Version'
6568             # 'Tool Name'
6569             # 'ISRC'
6570             # 'HDCD'
6571             # 'Waveform'
6572             );
6573              
6574             # iTunes audio encoding parameters
6575             # ref https://developer.apple.com/library/mac/#documentation/MusicAudio/Reference/AudioCodecServicesRef/Reference/reference.html
6576             %Image::ExifTool::QuickTime::EncodingParams = (
6577             PROCESS_PROC => \&ProcessEncodingParams,
6578             GROUPS => { 2 => 'Audio' },
6579             # (I have commented out the ones that don't have integer values because they
6580             # probably don't appear, and definitely wouldn't work with current decoding - PH)
6581              
6582             # global codec properties
6583             #'lnam' => 'AudioCodecName',
6584             #'lmak' => 'AudioCodecManufacturer',
6585             #'lfor' => 'AudioCodecFormat',
6586             'vpk?' => 'AudioHasVariablePacketByteSizes',
6587             #'ifm#' => 'AudioSupportedInputFormats',
6588             #'ofm#' => 'AudioSupportedOutputFormats',
6589             #'aisr' => 'AudioAvailableInputSampleRates',
6590             #'aosr' => 'AudioAvailableOutputSampleRates',
6591             'abrt' => 'AudioAvailableBitRateRange',
6592             'mnip' => 'AudioMinimumNumberInputPackets',
6593             'mnop' => 'AudioMinimumNumberOutputPackets',
6594             'cmnc' => 'AudioAvailableNumberChannels',
6595             'lmrc' => 'AudioDoesSampleRateConversion',
6596             #'aicl' => 'AudioAvailableInputChannelLayoutTags',
6597             #'aocl' => 'AudioAvailableOutputChannelLayoutTags',
6598             #'if4o' => 'AudioInputFormatsForOutputFormat',
6599             #'of4i' => 'AudioOutputFormatsForInputFormat',
6600             #'acfi' => 'AudioFormatInfo',
6601              
6602             # instance codec properties
6603             'tbuf' => 'AudioInputBufferSize',
6604             'pakf' => 'AudioPacketFrameSize',
6605             'pakb' => 'AudioMaximumPacketByteSize',
6606             #'ifmt' => 'AudioCurrentInputFormat',
6607             #'ofmt' => 'AudioCurrentOutputFormat',
6608             #'kuki' => 'AudioMagicCookie',
6609             'ubuf' => 'AudioUsedInputBufferSize',
6610             'init' => 'AudioIsInitialized',
6611             'brat' => 'AudioCurrentTargetBitRate',
6612             #'cisr' => 'AudioCurrentInputSampleRate',
6613             #'cosr' => 'AudioCurrentOutputSampleRate',
6614             'srcq' => 'AudioQualitySetting',
6615             #'brta' => 'AudioApplicableBitRateRange',
6616             #'isra' => 'AudioApplicableInputSampleRates',
6617             #'osra' => 'AudioApplicableOutputSampleRates',
6618             'pad0' => 'AudioZeroFramesPadded',
6619             'prmm' => 'AudioCodecPrimeMethod',
6620             #'prim' => 'AudioCodecPrimeInfo',
6621             #'icl ' => 'AudioInputChannelLayout',
6622             #'ocl ' => 'AudioOutputChannelLayout',
6623             #'acs ' => 'AudioCodecSettings',
6624             #'acfl' => 'AudioCodecFormatList',
6625             'acbf' => 'AudioBitRateControlMode',
6626             'vbrq' => 'AudioVBRQuality',
6627             'mdel' => 'AudioMinimumDelayMode',
6628              
6629             # deprecated
6630             'pakd' => 'AudioRequiresPacketDescription',
6631             #'brt#' => 'AudioAvailableBitRates',
6632             'acef' => 'AudioExtendFrequencies',
6633             'ursr' => 'AudioUseRecommendedSampleRate',
6634             'oppr' => 'AudioOutputPrecedence',
6635             #'loud' => 'AudioCurrentLoudnessStatistics',
6636              
6637             # others
6638             'vers' => 'AudioEncodingParamsVersion', #PH
6639             'cdcv' => { #PH
6640             Name => 'AudioComponentVersion',
6641             ValueConv => 'join ".", unpack("ncc", pack("N",$val))',
6642             },
6643             );
6644              
6645             # print to video data block
6646             %Image::ExifTool::QuickTime::Video = (
6647             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
6648             GROUPS => { 2 => 'Video' },
6649             0 => {
6650             Name => 'DisplaySize',
6651             PrintConv => {
6652             0 => 'Normal',
6653             1 => 'Double Size',
6654             2 => 'Half Size',
6655             3 => 'Full Screen',
6656             4 => 'Current Size',
6657             },
6658             },
6659             6 => {
6660             Name => 'SlideShow',
6661             PrintConv => {
6662             0 => 'No',
6663             1 => 'Yes',
6664             },
6665             },
6666             );
6667              
6668             # 'hnti' atoms
6669             %Image::ExifTool::QuickTime::HintInfo = (
6670             PROCESS_PROC => \&ProcessMOV,
6671             GROUPS => { 2 => 'Video' },
6672             'rtp ' => {
6673             Name => 'RealtimeStreamingProtocol',
6674             PrintConv => '$val=~s/^sdp /(SDP) /; $val',
6675             },
6676             'sdp ' => 'StreamingDataProtocol',
6677             );
6678              
6679             # 'hinf' atoms
6680             %Image::ExifTool::QuickTime::HintTrackInfo = (
6681             PROCESS_PROC => \&ProcessMOV,
6682             GROUPS => { 2 => 'Video' },
6683             trpY => { Name => 'TotalBytes', Format => 'int64u' }, #(documented)
6684             trpy => { Name => 'TotalBytes', Format => 'int64u' }, #(observed)
6685             totl => { Name => 'TotalBytes', Format => 'int32u' },
6686             nump => { Name => 'NumPackets', Format => 'int64u' },
6687             npck => { Name => 'NumPackets', Format => 'int32u' },
6688             tpyl => { Name => 'TotalBytesNoRTPHeaders', Format => 'int64u' },
6689             tpaY => { Name => 'TotalBytesNoRTPHeaders', Format => 'int32u' }, #(documented)
6690             tpay => { Name => 'TotalBytesNoRTPHeaders', Format => 'int32u' }, #(observed)
6691             maxr => {
6692             Name => 'MaxDataRate',
6693             Format => 'int32u',
6694             Count => 2,
6695             PrintConv => 'my @a=split(" ",$val);sprintf("%d bytes in %.3f s",$a[1],$a[0]/1000)',
6696             },
6697             dmed => { Name => 'MediaTrackBytes', Format => 'int64u' },
6698             dimm => { Name => 'ImmediateDataBytes', Format => 'int64u' },
6699             drep => { Name => 'RepeatedDataBytes', Format => 'int64u' },
6700             tmin => {
6701             Name => 'MinTransmissionTime',
6702             Format => 'int32u',
6703             PrintConv => 'sprintf("%.3f s",$val/1000)',
6704             },
6705             tmax => {
6706             Name => 'MaxTransmissionTime',
6707             Format => 'int32u',
6708             PrintConv => 'sprintf("%.3f s",$val/1000)',
6709             },
6710             pmax => { Name => 'LargestPacketSize', Format => 'int32u' },
6711             dmax => {
6712             Name => 'LargestPacketDuration',
6713             Format => 'int32u',
6714             PrintConv => 'sprintf("%.3f s",$val/1000)',
6715             },
6716             payt => {
6717             Name => 'PayloadType',
6718             Format => 'undef', # (necessary to prevent decoding as string!)
6719             ValueConv => 'unpack("N",$val) . " " . substr($val, 5)',
6720             PrintConv => '$val=~s/ /, /;$val',
6721             },
6722             );
6723              
6724             # MP4 media box (ref 5)
6725             %Image::ExifTool::QuickTime::Media = (
6726             PROCESS_PROC => \&ProcessMOV,
6727             WRITE_PROC => \&WriteQuickTime,
6728             GROUPS => { 1 => 'Track#', 2 => 'Video' },
6729             NOTES => 'MP4 media box.',
6730             mdhd => {
6731             Name => 'MediaHeader',
6732             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MediaHeader' },
6733             },
6734             hdlr => {
6735             Name => 'Handler',
6736             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Handler' },
6737             },
6738             minf => {
6739             Name => 'MediaInfo',
6740             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MediaInfo' },
6741             },
6742             );
6743              
6744             # MP4 media header box (ref 5)
6745             %Image::ExifTool::QuickTime::MediaHeader = (
6746             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
6747             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
6748             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
6749             GROUPS => { 1 => 'Track#', 2 => 'Video' },
6750             FORMAT => 'int32u',
6751             DATAMEMBER => [ 0, 1, 2, 3, 4 ],
6752             0 => {
6753             Name => 'MediaHeaderVersion',
6754             RawConv => '$$self{MediaHeaderVersion} = $val',
6755             },
6756             1 => {
6757             Name => 'MediaCreateDate',
6758             Groups => { 2 => 'Time' },
6759             %timeInfo,
6760             # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
6761             Hook => '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
6762             },
6763             2 => {
6764             Name => 'MediaModifyDate',
6765             Groups => { 2 => 'Time' },
6766             %timeInfo,
6767             # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
6768             Hook => '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
6769             },
6770             3 => {
6771             Name => 'MediaTimeScale',
6772             RawConv => '$$self{MediaTS} = $val',
6773             },
6774             4 => {
6775             Name => 'MediaDuration',
6776             RawConv => '$$self{MediaTS} ? $val / $$self{MediaTS} : $val',
6777             PrintConv => '$$self{MediaTS} ? ConvertDuration($val) : $val',
6778             # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
6779             Hook => '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
6780             },
6781             5 => {
6782             Name => 'MediaLanguageCode',
6783             Format => 'int16u',
6784             RawConv => '$val ? $val : undef',
6785             # allow both Macintosh (for MOV files) and ISO (for MP4 files) language codes
6786             ValueConv => '($val < 0x400 or $val == 0x7fff) ? $val : pack "C*", map { (($val>>$_)&0x1f)+0x60 } 10, 5, 0',
6787             PrintConv => q{
6788             return $val unless $val =~ /^\d+$/;
6789             require Image::ExifTool::Font;
6790             return $Image::ExifTool::Font::ttLang{Macintosh}{$val} || "Unknown ($val)";
6791             },
6792             },
6793             );
6794              
6795             # MP4 media information box (ref 5)
6796             %Image::ExifTool::QuickTime::MediaInfo = (
6797             PROCESS_PROC => \&ProcessMOV,
6798             WRITE_PROC => \&WriteQuickTime,
6799             GROUPS => { 1 => 'Track#', 2 => 'Video' },
6800             NOTES => 'MP4 media info box.',
6801             vmhd => {
6802             Name => 'VideoHeader',
6803             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::VideoHeader' },
6804             },
6805             smhd => {
6806             Name => 'AudioHeader',
6807             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AudioHeader' },
6808             },
6809             hmhd => {
6810             Name => 'HintHeader',
6811             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HintHeader' },
6812             },
6813             nmhd => {
6814             Name => 'NullMediaHeader',
6815             Flags => ['Binary','Unknown'],
6816             },
6817             dinf => {
6818             Name => 'DataInfo', # (don't change this name -- used to recognize directory when writing)
6819             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::DataInfo' },
6820             },
6821             gmhd => {
6822             Name => 'GenMediaHeader',
6823             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::GenMediaHeader' },
6824             },
6825             hdlr => { #PH
6826             Name => 'Handler',
6827             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Handler' },
6828             },
6829             stbl => {
6830             Name => 'SampleTable',
6831             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SampleTable' },
6832             },
6833             );
6834              
6835             # MP4 video media header (ref 5)
6836             %Image::ExifTool::QuickTime::VideoHeader = (
6837             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
6838             GROUPS => { 2 => 'Video' },
6839             NOTES => 'MP4 video media header.',
6840             FORMAT => 'int16u',
6841             2 => {
6842             Name => 'GraphicsMode',
6843             PrintHex => 1,
6844             SeparateTable => 'GraphicsMode',
6845             PrintConv => \%graphicsMode,
6846             },
6847             3 => { Name => 'OpColor', Format => 'int16u[3]' },
6848             );
6849              
6850             # MP4 audio media header (ref 5)
6851             %Image::ExifTool::QuickTime::AudioHeader = (
6852             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
6853             GROUPS => { 2 => 'Audio' },
6854             NOTES => 'MP4 audio media header.',
6855             FORMAT => 'int16u',
6856             2 => { Name => 'Balance', Format => 'fixed16s' },
6857             );
6858              
6859             # MP4 hint media header (ref 5)
6860             %Image::ExifTool::QuickTime::HintHeader = (
6861             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
6862             NOTES => 'MP4 hint media header.',
6863             FORMAT => 'int16u',
6864             2 => 'MaxPDUSize',
6865             3 => 'AvgPDUSize',
6866             4 => { Name => 'MaxBitrate', Format => 'int32u', PrintConv => 'ConvertBitrate($val)' },
6867             6 => { Name => 'AvgBitrate', Format => 'int32u', PrintConv => 'ConvertBitrate($val)' },
6868             );
6869              
6870             # MP4 sample table box (ref 5)
6871             %Image::ExifTool::QuickTime::SampleTable = (
6872             PROCESS_PROC => \&ProcessMOV,
6873             WRITE_PROC => \&WriteQuickTime,
6874             GROUPS => { 2 => 'Video' },
6875             NOTES => 'MP4 sample table box.',
6876             stsd => [
6877             {
6878             Name => 'AudioSampleDesc',
6879             Condition => '$$self{HandlerType} and $$self{HandlerType} eq "soun"',
6880             SubDirectory => {
6881             TagTable => 'Image::ExifTool::QuickTime::AudioSampleDesc',
6882             ProcessProc => \&ProcessSampleDesc,
6883             },
6884             },{
6885             Name => 'VideoSampleDesc',
6886             Condition => '$$self{HandlerType} and $$self{HandlerType} eq "vide"',
6887             SubDirectory => {
6888             TagTable => 'Image::ExifTool::QuickTime::ImageDesc',
6889             ProcessProc => \&ProcessSampleDesc,
6890             },
6891             },{
6892             Name => 'HintSampleDesc',
6893             Condition => '$$self{HandlerType} and $$self{HandlerType} eq "hint"',
6894             SubDirectory => {
6895             TagTable => 'Image::ExifTool::QuickTime::HintSampleDesc',
6896             ProcessProc => \&ProcessSampleDesc,
6897             },
6898             },{
6899             Name => 'MetaSampleDesc',
6900             Condition => '$$self{HandlerType} and $$self{HandlerType} eq "meta"',
6901             SubDirectory => {
6902             TagTable => 'Image::ExifTool::QuickTime::MetaSampleDesc',
6903             ProcessProc => \&ProcessSampleDesc,
6904             },
6905             },{
6906             Name => 'OtherSampleDesc',
6907             SubDirectory => {
6908             TagTable => 'Image::ExifTool::QuickTime::OtherSampleDesc',
6909             ProcessProc => \&ProcessSampleDesc,
6910             },
6911             },
6912             # (Note: "alis" HandlerType handled by the parent audio or video handler)
6913             ],
6914             stts => [ # decoding time-to-sample table
6915             {
6916             Name => 'VideoFrameRate',
6917             Notes => 'average rate calculated from time-to-sample table for video media',
6918             Condition => '$$self{HandlerType} and $$self{HandlerType} eq "vide"',
6919             Format => 'undef', # (necessary to prevent decoding as string!)
6920             # (must be RawConv so appropriate MediaTS is used in calculation)
6921             RawConv => 'Image::ExifTool::QuickTime::CalcSampleRate($self, \$val)',
6922             PrintConv => 'int($val * 1000 + 0.5) / 1000',
6923             },
6924             {
6925             Name => 'TimeToSampleTable',
6926             Flags => ['Binary','Unknown'],
6927             },
6928             ],
6929             ctts => {
6930             Name => 'CompositionTimeToSample',
6931             Flags => ['Binary','Unknown'],
6932             },
6933             stsc => {
6934             Name => 'SampleToChunk',
6935             Flags => ['Binary','Unknown'],
6936             },
6937             stsz => {
6938             Name => 'SampleSizes',
6939             Flags => ['Binary','Unknown'],
6940             },
6941             stz2 => {
6942             Name => 'CompactSampleSizes',
6943             Flags => ['Binary','Unknown'],
6944             },
6945             stco => {
6946             Name => 'ChunkOffset',
6947             Flags => ['Binary','Unknown'],
6948             },
6949             co64 => {
6950             Name => 'ChunkOffset64',
6951             Flags => ['Binary','Unknown'],
6952             },
6953             stss => {
6954             Name => 'SyncSampleTable',
6955             Flags => ['Binary','Unknown'],
6956             },
6957             stsh => {
6958             Name => 'ShadowSyncSampleTable',
6959             Flags => ['Binary','Unknown'],
6960             },
6961             padb => {
6962             Name => 'SamplePaddingBits',
6963             Flags => ['Binary','Unknown'],
6964             },
6965             stdp => {
6966             Name => 'SampleDegradationPriority',
6967             Flags => ['Binary','Unknown'],
6968             },
6969             sdtp => {
6970             Name => 'IdependentAndDisposableSamples',
6971             Flags => ['Binary','Unknown'],
6972             },
6973             sbgp => {
6974             Name => 'SampleToGroup',
6975             Flags => ['Binary','Unknown'],
6976             },
6977             sgpd => {
6978             Name => 'SampleGroupDescription',
6979             Flags => ['Binary','Unknown'],
6980             # bytes 4-7 give grouping type (ref ISO/IEC 14496-15:2014)
6981             # tsas - temporal sublayer sample
6982             # stsa - step-wise temporal layer access
6983             # avss - AVC sample
6984             # tscl - temporal layer scalability
6985             # sync - sync sample
6986             },
6987             subs => {
6988             Name => 'Sub-sampleInformation',
6989             Flags => ['Binary','Unknown'],
6990             },
6991             cslg => {
6992             Name => 'CompositionToDecodeTimelineMapping',
6993             Flags => ['Binary','Unknown'],
6994             },
6995             stps => {
6996             Name => 'PartialSyncSamples',
6997             ValueConv => 'join " ",unpack("x8N*",$val)',
6998             },
6999             # mark - 8 bytes all zero (GoPro)
7000             );
7001              
7002             # MP4 audio sample description box (ref 5/AtomicParsley 0.9.4 parsley.cpp)
7003             %Image::ExifTool::QuickTime::AudioSampleDesc = (
7004             PROCESS_PROC => \&ProcessHybrid,
7005             VARS => { ID_LABEL => 'ID/Index' },
7006             GROUPS => { 2 => 'Audio' },
7007             NOTES => q{
7008             MP4 audio sample description. This hybrid atom contains both data and child
7009             atoms.
7010             },
7011             4 => {
7012             Name => 'AudioFormat',
7013             Format => 'undef[4]',
7014             RawConv => q{
7015             $$self{AudioFormat} = $val;
7016             return undef unless $val =~ /^[\w ]{4}$/i;
7017             # check for protected audio format
7018             $self->OverrideFileType('M4P') if $val eq 'drms' and $$self{VALUE}{FileType} eq 'M4A';
7019             return $val;
7020             },
7021             # see this link for print conversions (not complete):
7022             # https://github.com/yannickcr/brooser/blob/master/php/librairies/getid3/module.audio-video.quicktime.php
7023             },
7024             20 => { #PH
7025             Name => 'AudioVendorID',
7026             Condition => '$$self{AudioFormat} ne "mp4s"',
7027             Format => 'undef[4]',
7028             RawConv => '$val eq "\0\0\0\0" ? undef : $val',
7029             PrintConv => \%vendorID,
7030             SeparateTable => 'VendorID',
7031             },
7032             24 => { Name => 'AudioChannels', Format => 'int16u' },
7033             26 => { Name => 'AudioBitsPerSample', Format => 'int16u' },
7034             32 => { Name => 'AudioSampleRate', Format => 'fixed32u' },
7035             #
7036             # Observed offsets for child atoms of various AudioFormat types:
7037             #
7038             # AudioFormat Offset Child atoms
7039             # ----------- ------ ----------------
7040             # mp4a 52 * wave, chan, esds, SA3D(Insta360 spherical video params?,also GoPro Max)
7041             # in24 52 wave, chan
7042             # "ms\0\x11" 52 wave
7043             # sowt 52 chan
7044             # mp4a 36 * esds, pinf
7045             # drms 36 esds, sinf
7046             # samr 36 damr
7047             # alac 36 alac
7048             # ac-3 36 dac3
7049             #
7050             # (* child atoms found at different offsets in mp4a)
7051             #
7052             pinf => {
7053             Name => 'PurchaseInfo',
7054             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ProtectionInfo' },
7055             },
7056             sinf => { # "protection scheme information"
7057             Name => 'ProtectionInfo', #3
7058             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ProtectionInfo' },
7059             },
7060             # f - 16/36 bytes
7061             # esds - 31/40/42/43 bytes - ES descriptor (ref 3)
7062             damr => { #3
7063             Name => 'DecodeConfig',
7064             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::DecodeConfig' },
7065             },
7066             wave => {
7067             Name => 'Wave',
7068             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Wave' },
7069             },
7070             chan => {
7071             Name => 'AudioChannelLayout',
7072             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ChannelLayout' },
7073             }
7074             # alac - 28 bytes
7075             # adrm - AAX DRM atom? 148 bytes
7076             # aabd - AAX unknown 17kB (contains 'aavd' strings)
7077             # SA3D - written by Garmin VIRB360
7078             );
7079              
7080             # AMR decode config box (ref 3)
7081             %Image::ExifTool::QuickTime::DecodeConfig = (
7082             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7083             GROUPS => { 2 => 'Audio' },
7084             0 => {
7085             Name => 'EncoderVendor',
7086             Format => 'undef[4]',
7087             },
7088             4 => 'EncoderVersion',
7089             # 5 - int16u - packet modes
7090             # 7 - int8u - number of packet mode changes
7091             # 8 - int8u - bytes per packet
7092             );
7093              
7094             %Image::ExifTool::QuickTime::ProtectionInfo = (
7095             PROCESS_PROC => \&ProcessMOV,
7096             GROUPS => { 2 => 'Audio' },
7097             NOTES => 'Child atoms found in "sinf" and/or "pinf" atoms.',
7098             frma => 'OriginalFormat',
7099             # imif - IPMP information
7100             schm => {
7101             Name => 'SchemeType',
7102             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SchemeType' },
7103             },
7104             schi => {
7105             Name => 'SchemeInfo',
7106             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SchemeInfo' },
7107             },
7108             enda => {
7109             Name => 'Endianness',
7110             Format => 'int16u',
7111             PrintConv => {
7112             0 => 'Big-endian (Motorola, MM)',
7113             1 => 'Little-endian (Intel, II)',
7114             },
7115             },
7116             # skcr
7117             );
7118              
7119             %Image::ExifTool::QuickTime::Wave = (
7120             PROCESS_PROC => \&ProcessMOV,
7121             frma => 'PurchaseFileFormat',
7122             enda => {
7123             Name => 'Endianness',
7124             Format => 'int16u',
7125             PrintConv => {
7126             0 => 'Big-endian (Motorola, MM)',
7127             1 => 'Little-endian (Intel, II)',
7128             },
7129             },
7130             # "ms\0\x11" - 20 bytes
7131             );
7132              
7133             # audio channel layout (ref CoreAudioTypes.h)
7134             %Image::ExifTool::QuickTime::ChannelLayout = (
7135             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7136             GROUPS => { 2 => 'Audio' },
7137             DATAMEMBER => [ 0, 8 ],
7138             NOTES => 'Audio channel layout.',
7139             # 0 - version and flags
7140             4 => {
7141             Name => 'LayoutFlags',
7142             Format => 'int16u',
7143             RawConv => '$$self{LayoutFlags} = $val',
7144             PrintConvColumns => 2,
7145             PrintConv => {
7146             0 => 'UseDescriptions',
7147             1 => 'UseBitmap',
7148             100 => 'Mono',
7149             101 => 'Stereo',
7150             102 => 'StereoHeadphones',
7151             100 => 'Mono',
7152             101 => 'Stereo',
7153             102 => 'StereoHeadphones',
7154             103 => 'MatrixStereo',
7155             104 => 'MidSide',
7156             105 => 'XY',
7157             106 => 'Binaural',
7158             107 => 'Ambisonic_B_Format',
7159             108 => 'Quadraphonic',
7160             109 => 'Pentagonal',
7161             110 => 'Hexagonal',
7162             111 => 'Octagonal',
7163             112 => 'Cube',
7164             113 => 'MPEG_3_0_A',
7165             114 => 'MPEG_3_0_B',
7166             115 => 'MPEG_4_0_A',
7167             116 => 'MPEG_4_0_B',
7168             117 => 'MPEG_5_0_A',
7169             118 => 'MPEG_5_0_B',
7170             119 => 'MPEG_5_0_C',
7171             120 => 'MPEG_5_0_D',
7172             121 => 'MPEG_5_1_A',
7173             122 => 'MPEG_5_1_B',
7174             123 => 'MPEG_5_1_C',
7175             124 => 'MPEG_5_1_D',
7176             125 => 'MPEG_6_1_A',
7177             126 => 'MPEG_7_1_A',
7178             127 => 'MPEG_7_1_B',
7179             128 => 'MPEG_7_1_C',
7180             129 => 'Emagic_Default_7_1',
7181             130 => 'SMPTE_DTV',
7182             131 => 'ITU_2_1',
7183             132 => 'ITU_2_2',
7184             133 => 'DVD_4',
7185             134 => 'DVD_5',
7186             135 => 'DVD_6',
7187             136 => 'DVD_10',
7188             137 => 'DVD_11',
7189             138 => 'DVD_18',
7190             139 => 'AudioUnit_6_0',
7191             140 => 'AudioUnit_7_0',
7192             141 => 'AAC_6_0',
7193             142 => 'AAC_6_1',
7194             143 => 'AAC_7_0',
7195             144 => 'AAC_Octagonal',
7196             145 => 'TMH_10_2_std',
7197             146 => 'TMH_10_2_full',
7198             147 => 'DiscreteInOrder',
7199             148 => 'AudioUnit_7_0_Front',
7200             149 => 'AC3_1_0_1',
7201             150 => 'AC3_3_0',
7202             151 => 'AC3_3_1',
7203             152 => 'AC3_3_0_1',
7204             153 => 'AC3_2_1_1',
7205             154 => 'AC3_3_1_1',
7206             155 => 'EAC_6_0_A',
7207             156 => 'EAC_7_0_A',
7208             157 => 'EAC3_6_1_A',
7209             158 => 'EAC3_6_1_B',
7210             159 => 'EAC3_6_1_C',
7211             160 => 'EAC3_7_1_A',
7212             161 => 'EAC3_7_1_B',
7213             162 => 'EAC3_7_1_C',
7214             163 => 'EAC3_7_1_D',
7215             164 => 'EAC3_7_1_E',
7216             165 => 'EAC3_7_1_F',
7217             166 => 'EAC3_7_1_G',
7218             167 => 'EAC3_7_1_H',
7219             168 => 'DTS_3_1',
7220             169 => 'DTS_4_1',
7221             170 => 'DTS_6_0_A',
7222             171 => 'DTS_6_0_B',
7223             172 => 'DTS_6_0_C',
7224             173 => 'DTS_6_1_A',
7225             174 => 'DTS_6_1_B',
7226             175 => 'DTS_6_1_C',
7227             176 => 'DTS_7_0',
7228             177 => 'DTS_7_1',
7229             178 => 'DTS_8_0_A',
7230             179 => 'DTS_8_0_B',
7231             180 => 'DTS_8_1_A',
7232             181 => 'DTS_8_1_B',
7233             182 => 'DTS_6_1_D',
7234             183 => 'AAC_7_1_B',
7235             0xffff => 'Unknown',
7236             },
7237             },
7238             6 => {
7239             Name => 'AudioChannels',
7240             Condition => '$$self{LayoutFlags} != 0 and $$self{LayoutFlags} != 1',
7241             Format => 'int16u',
7242             },
7243             8 => {
7244             Name => 'AudioChannelTypes',
7245             Condition => '$$self{LayoutFlags} == 1',
7246             Format => 'int32u',
7247             PrintConv => { BITMASK => {
7248             0 => 'Left',
7249             1 => 'Right',
7250             2 => 'Center',
7251             3 => 'LFEScreen',
7252             4 => 'LeftSurround',
7253             5 => 'RightSurround',
7254             6 => 'LeftCenter',
7255             7 => 'RightCenter',
7256             8 => 'CenterSurround',
7257             9 => 'LeftSurroundDirect',
7258             10 => 'RightSurroundDirect',
7259             11 => 'TopCenterSurround',
7260             12 => 'VerticalHeightLeft',
7261             13 => 'VerticalHeightCenter',
7262             14 => 'VerticalHeightRight',
7263             15 => 'TopBackLeft',
7264             16 => 'TopBackCenter',
7265             17 => 'TopBackRight',
7266             }},
7267             },
7268             12 => {
7269             Name => 'NumChannelDescriptions',
7270             Condition => '$$self{LayoutFlags} == 1',
7271             Format => 'int32u',
7272             RawConv => '$$self{NumChannelDescriptions} = $val',
7273             },
7274             16 => {
7275             Name => 'Channel1Label',
7276             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 0',
7277             Format => 'int32u',
7278             SeparateTable => 'ChannelLabel',
7279             PrintConv => \%channelLabel,
7280             },
7281             20 => {
7282             Name => 'Channel1Flags',
7283             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 0',
7284             Format => 'int32u',
7285             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7286             },
7287             24 => {
7288             Name => 'Channel1Coordinates',
7289             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 0',
7290             Notes => q{
7291             3 numbers: for rectangular coordinates left/right, back/front, down/up; for
7292             spherical coordinates left/right degrees, down/up degrees, distance
7293             },
7294             Format => 'float[3]',
7295             },
7296             36 => {
7297             Name => 'Channel2Label',
7298             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 1',
7299             Format => 'int32u',
7300             SeparateTable => 'ChannelLabel',
7301             PrintConv => \%channelLabel,
7302             },
7303             40 => {
7304             Name => 'Channel2Flags',
7305             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 1',
7306             Format => 'int32u',
7307             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7308             },
7309             44 => {
7310             Name => 'Channel2Coordinates',
7311             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 1',
7312             Format => 'float[3]',
7313             },
7314             56 => {
7315             Name => 'Channel3Label',
7316             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 2',
7317             Format => 'int32u',
7318             SeparateTable => 'ChannelLabel',
7319             PrintConv => \%channelLabel,
7320             },
7321             60 => {
7322             Name => 'Channel3Flags',
7323             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 2',
7324             Format => 'int32u',
7325             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7326             },
7327             64 => {
7328             Name => 'Channel3Coordinates',
7329             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 2',
7330             Format => 'float[3]',
7331             },
7332             76 => {
7333             Name => 'Channel4Label',
7334             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 3',
7335             Format => 'int32u',
7336             SeparateTable => 'ChannelLabel',
7337             PrintConv => \%channelLabel,
7338             },
7339             80 => {
7340             Name => 'Channel4Flags',
7341             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 3',
7342             Format => 'int32u',
7343             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7344             },
7345             84 => {
7346             Name => 'Channel4Coordinates',
7347             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 3',
7348             Format => 'float[3]',
7349             },
7350             96 => {
7351             Name => 'Channel5Label',
7352             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 4',
7353             Format => 'int32u',
7354             SeparateTable => 'ChannelLabel',
7355             PrintConv => \%channelLabel,
7356             },
7357             100 => {
7358             Name => 'Channel5Flags',
7359             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 4',
7360             Format => 'int32u',
7361             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7362             },
7363             104 => {
7364             Name => 'Channel5Coordinates',
7365             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 4',
7366             Format => 'float[3]',
7367             },
7368             116 => {
7369             Name => 'Channel6Label',
7370             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 5',
7371             Format => 'int32u',
7372             SeparateTable => 'ChannelLabel',
7373             PrintConv => \%channelLabel,
7374             },
7375             120 => {
7376             Name => 'Channel6Flags',
7377             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 5',
7378             Format => 'int32u',
7379             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7380             },
7381             124 => {
7382             Name => 'Channel6Coordinates',
7383             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 5',
7384             Format => 'float[3]',
7385             },
7386             136 => {
7387             Name => 'Channel7Label',
7388             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 6',
7389             Format => 'int32u',
7390             SeparateTable => 'ChannelLabel',
7391             PrintConv => \%channelLabel,
7392             },
7393             140 => {
7394             Name => 'Channel7Flags',
7395             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 6',
7396             Format => 'int32u',
7397             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7398             },
7399             144 => {
7400             Name => 'Channel7Coordinates',
7401             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 6',
7402             Format => 'float[3]',
7403             },
7404             156 => {
7405             Name => 'Channel8Label',
7406             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 7',
7407             Format => 'int32u',
7408             SeparateTable => 'ChannelLabel',
7409             PrintConv => \%channelLabel,
7410             },
7411             160 => {
7412             Name => 'Channel8Flags',
7413             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 7',
7414             Format => 'int32u',
7415             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
7416             },
7417             164 => {
7418             Name => 'Channel8Coordinates',
7419             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 7',
7420             Format => 'float[3]',
7421             },
7422             # (arbitrarily decode only first 8 channels)
7423             );
7424              
7425             # scheme type atom
7426             # ref http://xhelmboyx.tripod.com/formats/mp4-layout.txt
7427             %Image::ExifTool::QuickTime::SchemeType = (
7428             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7429             GROUPS => { 2 => 'Audio' },
7430             # 0 - 4 bytes version
7431             4 => { Name => 'SchemeType', Format => 'undef[4]' },
7432             8 => { Name => 'SchemeVersion', Format => 'int16u' },
7433             10 => { Name => 'SchemeURL', Format => 'string[$size-10]' },
7434             );
7435              
7436             %Image::ExifTool::QuickTime::SchemeInfo = (
7437             PROCESS_PROC => \&ProcessMOV,
7438             GROUPS => { 2 => 'Audio' },
7439             user => {
7440             Name => 'UserID',
7441             Groups => { 2 => 'Author' },
7442             ValueConv => '"0x" . unpack("H*",$val)',
7443             },
7444             cert => { # ref http://www.onvif.org/specs/stream/ONVIF-ExportFileFormat-Spec-v100.pdf
7445             Name => 'Certificate',
7446             ValueConv => '"0x" . unpack("H*",$val)',
7447             },
7448             'key ' => {
7449             Name => 'KeyID',
7450             ValueConv => '"0x" . unpack("H*",$val)',
7451             },
7452             iviv => {
7453             Name => 'InitializationVector',
7454             ValueConv => 'unpack("H*",$val)',
7455             },
7456             righ => {
7457             Name => 'Rights',
7458             Groups => { 2 => 'Author' },
7459             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Rights' },
7460             },
7461             name => { Name => 'UserName', Groups => { 2 => 'Author' } },
7462             # chtb
7463             # priv - private data
7464             # sign
7465             # adkm - Adobe DRM key management system (ref http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf)
7466             # iKMS
7467             # iSFM
7468             # iSLT
7469             );
7470              
7471             %Image::ExifTool::QuickTime::Rights = (
7472             PROCESS_PROC => \&ProcessRights,
7473             GROUPS => { 2 => 'Audio' },
7474             veID => 'ItemVendorID', #PH ("VendorID" ref 19)
7475             plat => 'Platform', #18?
7476             aver => 'VersionRestrictions', #19 ("appversion?" ref 18)
7477             tran => 'TransactionID', #18
7478             song => 'ItemID', #19 ("appid" ref 18)
7479             tool => {
7480             Name => 'ItemTool', #PH (guess) ("itunes build?" ref 18)
7481             Format => 'string',
7482             },
7483             medi => 'MediaFlags', #PH (?)
7484             mode => 'ModeFlags', #PH (?) 0x04 is HD flag (https://compilr.com/heksesang/requiem-mac/UnDrm.java)
7485             );
7486              
7487             # MP4 hint sample description box (ref 5)
7488             # (ref https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-SW1)
7489             %Image::ExifTool::QuickTime::HintSampleDesc = (
7490             PROCESS_PROC => \&ProcessHybrid,
7491             VARS => { ID_LABEL => 'ID/Index' },
7492             NOTES => 'MP4 hint sample description.',
7493             4 => { Name => 'HintFormat', Format => 'undef[4]' },
7494             # 14 - int16u DataReferenceIndex
7495             16 => { Name => 'HintTrackVersion', Format => 'int16u' },
7496             # 18 - int16u LastCompatibleHintTrackVersion
7497             20 => { Name => 'MaxPacketSize', Format => 'int32u' },
7498             #
7499             # Observed offsets for child atoms of various HintFormat types:
7500             #
7501             # HintFormat Offset Child atoms
7502             # ----------- ------ ----------------
7503             # "rtp " 24 tims
7504             #
7505             tims => { Name => 'RTPTimeScale', Format => 'int32u' },
7506             tsro => { Name => 'TimestampRandomOffset', Format => 'int32u' },
7507             snro => { Name => 'SequenceNumberRandomOffset', Format => 'int32u' },
7508             );
7509              
7510             # MP4 metadata sample description box
7511             %Image::ExifTool::QuickTime::MetaSampleDesc = (
7512             PROCESS_PROC => \&ProcessHybrid,
7513             NOTES => 'MP4 metadata sample description.',
7514             4 => {
7515             Name => 'MetaFormat',
7516             Format => 'undef[4]',
7517             RawConv => '$$self{MetaFormat} = $val',
7518             },
7519             #
7520             # Observed offsets for child atoms of various MetaFormat types:
7521             #
7522             # MetaFormat Offset Child atoms
7523             # ----------- ------ ----------------
7524             # mebx 24 keys,btrt,lidp,lidl
7525             # fdsc - -
7526             # gpmd - -
7527             # rtmd - -
7528             # CTMD - -
7529             #
7530             'keys' => { #PH (iPhone7+ hevc)
7531             Name => 'Keys',
7532             SubDirectory => {
7533             TagTable => 'Image::ExifTool::QuickTime::Keys',
7534             ProcessProc => \&ProcessMetaKeys,
7535             },
7536             },
7537             btrt => {
7538             Name => 'BitrateInfo',
7539             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Bitrate' },
7540             },
7541             );
7542              
7543             # MP4 generic sample description box
7544             %Image::ExifTool::QuickTime::OtherSampleDesc = (
7545             PROCESS_PROC => \&ProcessHybrid,
7546             4 => {
7547             Name => 'OtherFormat',
7548             Format => 'undef[4]',
7549             RawConv => '$$self{MetaFormat} = $val', # (yes, use MetaFormat for this too)
7550             },
7551             #
7552             # Observed offsets for child atoms of various OtherFormat types:
7553             #
7554             # OtherFormat Offset Child atoms
7555             # ----------- ------ ----------------
7556             # avc1 86 avcC
7557             # mp4a 36 esds
7558             # mp4s 16 esds
7559             # tmcd 34 name
7560             # data - -
7561             #
7562             ftab => { Name => 'FontTable', Format => 'undef', ValueConv => 'substr($val, 5)' },
7563             name => { Name => 'OtherName', Format => 'undef', ValueConv => 'substr($val, 4)' },
7564             # mrlh = GM header?
7565             # mrlv = GM data
7566             # mrld = GM data (448-byte records):
7567             # 0 - int32u count
7568             # 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=%
7569             # 8 - int32u ? (0,1,3,4,5)
7570             # 12 - string[64] units
7571             # 76 - int32u ? (1,3,7,15)
7572             # 80 - int32u 0
7573             # 84 - undef[4] ?
7574             # 88 - int16u[6] ?
7575             # 100 - undef[32] ?
7576             # 132 - string[64] measurement name
7577             # 196 - string[64] measurement name
7578             );
7579              
7580             # MP4 data information box (ref 5)
7581             %Image::ExifTool::QuickTime::DataInfo = (
7582             PROCESS_PROC => \&ProcessMOV,
7583             WRITE_PROC => \&WriteQuickTime, # (necessary to parse dref even though we don't change it)
7584             NOTES => 'MP4 data information box.',
7585             dref => {
7586             Name => 'DataRef',
7587             SubDirectory => {
7588             TagTable => 'Image::ExifTool::QuickTime::DataRef',
7589             Start => 8,
7590             },
7591             },
7592             );
7593              
7594             # Generic media header
7595             %Image::ExifTool::QuickTime::GenMediaHeader = (
7596             PROCESS_PROC => \&ProcessMOV,
7597             gmin => {
7598             Name => 'GenMediaInfo',
7599             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::GenMediaInfo' },
7600             },
7601             text => {
7602             Name => 'Text',
7603             Flags => ['Binary','Unknown'],
7604             },
7605             tmcd => {
7606             Name => 'TimeCode',
7607             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TimeCode' },
7608             },
7609             );
7610              
7611             # TimeCode header
7612             %Image::ExifTool::QuickTime::TimeCode = (
7613             PROCESS_PROC => \&ProcessMOV,
7614             tcmi => {
7615             Name => 'TCMediaInfo',
7616             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TCMediaInfo' },
7617             },
7618             );
7619              
7620             # TimeCode media info (ref 12)
7621             %Image::ExifTool::QuickTime::TCMediaInfo = (
7622             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7623             GROUPS => { 2 => 'Video' },
7624             4 => {
7625             Name => 'TextFont',
7626             Format => 'int16u',
7627             PrintConv => { 0 => 'System' },
7628             },
7629             6 => {
7630             Name => 'TextFace',
7631             Format => 'int16u',
7632             PrintConv => {
7633             0 => 'Plain',
7634             BITMASK => {
7635             0 => 'Bold',
7636             1 => 'Italic',
7637             2 => 'Underline',
7638             3 => 'Outline',
7639             4 => 'Shadow',
7640             5 => 'Condense',
7641             6 => 'Extend',
7642             },
7643             },
7644             },
7645             8 => {
7646             Name => 'TextSize',
7647             Format => 'int16u',
7648             },
7649             # 10 - reserved
7650             12 => {
7651             Name => 'TextColor',
7652             Format => 'int16u[3]',
7653             },
7654             18 => {
7655             Name => 'BackgroundColor',
7656             Format => 'int16u[3]',
7657             },
7658             24 => {
7659             Name => 'FontName',
7660             Format => 'pstring',
7661             ValueConv => '$self->Decode($val, $self->Options("CharsetQuickTime"))',
7662             },
7663             );
7664              
7665             # Generic media info (ref http://sourceforge.jp/cvs/view/ntvrec/ntvrec/libqtime/gmin.h?view=co)
7666             %Image::ExifTool::QuickTime::GenMediaInfo = (
7667             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7668             GROUPS => { 2 => 'Video' },
7669             0 => 'GenMediaVersion',
7670             1 => { Name => 'GenFlags', Format => 'int8u[3]' },
7671             4 => { Name => 'GenGraphicsMode',
7672             Format => 'int16u',
7673             PrintHex => 1,
7674             SeparateTable => 'GraphicsMode',
7675             PrintConv => \%graphicsMode,
7676             },
7677             6 => { Name => 'GenOpColor', Format => 'int16u[3]' },
7678             12 => { Name => 'GenBalance', Format => 'fixed16s' },
7679             );
7680              
7681             # MP4 data reference box (ref 5)
7682             %Image::ExifTool::QuickTime::DataRef = (
7683             PROCESS_PROC => \&ProcessMOV,
7684             WRITE_PROC => \&WriteQuickTime, # (necessary to parse dref even though we don't change it)
7685             NOTES => 'MP4 data reference box.',
7686             'url ' => {
7687             Name => 'URL',
7688             Format => 'undef', # (necessary to prevent decoding as string!)
7689             RawConv => q{
7690             # ignore if self-contained (flags bit 0 set)
7691             return undef if unpack("N",$val) & 0x01;
7692             $_ = substr($val,4); s/\0.*//s; $_;
7693             },
7694             },
7695             "url\0" => { # (written by GoPro)
7696             Name => 'URL',
7697             Format => 'undef', # (necessary to prevent decoding as string!)
7698             RawConv => q{
7699             # ignore if self-contained (flags bit 0 set)
7700             return undef if unpack("N",$val) & 0x01;
7701             $_ = substr($val,4); s/\0.*//s; $_;
7702             },
7703             },
7704             'urn ' => {
7705             Name => 'URN',
7706             Format => 'undef', # (necessary to prevent decoding as string!)
7707             RawConv => q{
7708             return undef if unpack("N",$val) & 0x01;
7709             $_ = substr($val,4); s/\0+/; /; s/\0.*//s; $_;
7710             },
7711             },
7712             );
7713              
7714             # MP4 handler box (ref 5)
7715             %Image::ExifTool::QuickTime::Handler = (
7716             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7717             GROUPS => { 2 => 'Video' },
7718             4 => { #PH
7719             Name => 'HandlerClass',
7720             Format => 'undef[4]',
7721             RawConv => '$val eq "\0\0\0\0" ? undef : $val',
7722             PrintConv => {
7723             mhlr => 'Media Handler',
7724             dhlr => 'Data Handler',
7725             },
7726             },
7727             8 => {
7728             Name => 'HandlerType',
7729             Format => 'undef[4]',
7730             RawConv => q{
7731             $$self{HandlerType} = $val unless $val eq 'alis' or $val eq 'url ';
7732             $$self{HasHandler}{$val} = 1; # remember all our handlers
7733             return $val;
7734             },
7735             PrintConvColumns => 2,
7736             PrintConv => {
7737             alis => 'Alias Data', #PH
7738             crsm => 'Clock Reference', #3
7739             hint => 'Hint Track',
7740             ipsm => 'IPMP', #3
7741             m7sm => 'MPEG-7 Stream', #3
7742             meta => 'NRT Metadata', #PH
7743             mdir => 'Metadata', #3
7744             mdta => 'Metadata Tags', #PH
7745             mjsm => 'MPEG-J', #3
7746             ocsm => 'Object Content', #3
7747             odsm => 'Object Descriptor', #3
7748             priv => 'Private', #PH
7749             sdsm => 'Scene Description', #3
7750             soun => 'Audio Track',
7751             text => 'Text', #PH (but what type? subtitle?)
7752             tmcd => 'Time Code', #PH
7753             'url '=> 'URL', #3
7754             vide => 'Video Track',
7755             subp => 'Subpicture', #http://www.google.nl/patents/US7778526
7756             nrtm => 'Non-Real Time Metadata', #PH (Sony ILCE-7S) [how is this different from "meta"?]
7757             pict => 'Picture', # (HEIC images)
7758             camm => 'Camera Metadata', # (Insta360 MP4)
7759             psmd => 'Panasonic Static Metadata', #PH (Leica C-Lux CAM-DC25)
7760             data => 'Data', #PH (GPS and G-sensor data from DataKam)
7761             sbtl => 'Subtitle', #PH (TomTom Bandit Action Cam)
7762             },
7763             },
7764             12 => { #PH
7765             Name => 'HandlerVendorID',
7766             Format => 'undef[4]',
7767             RawConv => '$val eq "\0\0\0\0" ? undef : $val',
7768             PrintConv => \%vendorID,
7769             SeparateTable => 'VendorID',
7770             },
7771             24 => {
7772             Name => 'HandlerDescription',
7773             Format => 'string',
7774             # (sometimes this is a Pascal string, and sometimes it is a C string)
7775             RawConv => q{
7776             $val=substr($val,1,ord($1)) if $val=~/^([\0-\x1f])/ and ord($1)
7777             length $val ? $val : undef;
7778             },
7779             },
7780             );
7781              
7782             # Flip uuid data (ref PH)
7783             %Image::ExifTool::QuickTime::Flip = (
7784             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7785             FORMAT => 'int32u',
7786             FIRST_ENTRY => 0,
7787             NOTES => 'Found in MP4 files from Flip Video cameras.',
7788             GROUPS => { 1 => 'MakerNotes', 2 => 'Image' },
7789             1 => 'PreviewImageWidth',
7790             2 => 'PreviewImageHeight',
7791             13 => 'PreviewImageLength',
7792             14 => { # (confirmed for FlipVideoMinoHD)
7793             Name => 'SerialNumber',
7794             Groups => { 2 => 'Camera' },
7795             Format => 'string[16]',
7796             },
7797             28 => {
7798             Name => 'PreviewImage',
7799             Groups => { 2 => 'Preview' },
7800             Format => 'undef[$val{13}]',
7801             RawConv => '$self->ValidateImage(\$val, $tag)',
7802             },
7803             );
7804              
7805             # atoms in Pittasoft "free" atom
7806             %Image::ExifTool::QuickTime::Pittasoft = (
7807             PROCESS_PROC => \&ProcessMOV,
7808             NOTES => 'Tags found in Pittasoft Blackvue dashcam "free" data.',
7809             cprt => 'Copyright',
7810             thum => {
7811             Name => 'PreviewImage',
7812             Groups => { 2 => 'Preview' },
7813             Binary => 1,
7814             RawConv => q{
7815             return undef unless length $val > 4;
7816             my $len = unpack('N', $val);
7817             return undef unless length $val >= 4 + $len;
7818             return substr($val, 4, $len);
7819             },
7820             },
7821             ptnm => {
7822             Name => 'OriginalFileName',
7823             ValueConv => 'substr($val, 4, -1)',
7824             },
7825             ptrh => {
7826             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Pittasoft' },
7827             # contains these atoms:
7828             # ptvi - 27 bytes: '..avc1...'
7829             # ptso - 16 bytes: '..mp4a...'
7830             },
7831             'gps ' => {
7832             Name => 'GPSLog',
7833             Binary => 1, # (ASCII NMEA track log with leading timestamps)
7834             Notes => 'parsed to extract GPS separately when ExtractEmbedded is used',
7835             RawConv => q{
7836             $val =~ s/\0+$//; # remove trailing nulls
7837             if (length $val and $$self{OPTIONS}{ExtractEmbedded}) {
7838             my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
7839             Image::ExifTool::QuickTime::ProcessGPSLog($self, { DataPt => \$val }, $tagTbl);
7840             }
7841             return $val;
7842             },
7843             },
7844             '3gf ' => {
7845             Name => 'AccelData',
7846             SubDirectory => {
7847             TagTable => 'Image::ExifTool::QuickTime::Stream',
7848             ProcessProc => \&Process_3gf,
7849             },
7850             },
7851             sttm => {
7852             Name => 'StartTime',
7853             Format => 'int64u',
7854             Groups => { 2 => 'Time' },
7855             RawConv => '$$self{StartTime} = $val',
7856             # (ms since Jan 1, 1970, in local time zone - PH)
7857             ValueConv => q{
7858             my $secs = int($val / 1000);
7859             return ConvertUnixTime($secs) . sprintf(".%03d",$val - $secs * 1000);
7860             },
7861             PrintConv => '$self->ConvertDateTime($val)',
7862             },
7863             );
7864              
7865             # QuickTime composite tags
7866             %Image::ExifTool::QuickTime::Composite = (
7867             GROUPS => { 2 => 'Video' },
7868             Rotation => {
7869             Notes => q{
7870             writing this tag updates QuickTime MatrixStructure for all tracks with a
7871             non-zero image size
7872             },
7873             Require => {
7874             0 => 'QuickTime:MatrixStructure',
7875             1 => 'QuickTime:HandlerType',
7876             },
7877             Writable => 1,
7878             Protected => 1,
7879             WriteAlso => {
7880             MatrixStructure => 'Image::ExifTool::QuickTime::GetRotationMatrix($val)',
7881             },
7882             ValueConv => 'Image::ExifTool::QuickTime::CalcRotation($self)',
7883             ValueConvInv => '$val',
7884             },
7885             AvgBitrate => {
7886             Priority => 0, # let QuickTime::AvgBitrate take priority
7887             Require => {
7888             0 => 'QuickTime::MediaDataSize',
7889             1 => 'QuickTime::Duration',
7890             },
7891             RawConv => q{
7892             return undef unless $val[1];
7893             $val[1] /= $$self{TimeScale} if $$self{TimeScale};
7894             my $key = 'MediaDataSize';
7895             my $size = $val[0];
7896             for (;;) {
7897             $key = $self->NextTagKey($key) or last;
7898             $size += $self->GetValue($key, 'ValueConv');
7899             }
7900             return int($size * 8 / $val[1] + 0.5);
7901             },
7902             PrintConv => 'ConvertBitrate($val)',
7903             },
7904             GPSLatitude => {
7905             Require => 'QuickTime:GPSCoordinates',
7906             Groups => { 2 => 'Location' },
7907             ValueConv => 'my @c = split " ", $val; $c[0]',
7908             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
7909             },
7910             GPSLongitude => {
7911             Require => 'QuickTime:GPSCoordinates',
7912             Groups => { 2 => 'Location' },
7913             ValueConv => 'my @c = split " ", $val; $c[1]',
7914             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
7915             },
7916             # split altitude into GPSAltitude/GPSAltitudeRef like EXIF and XMP
7917             GPSAltitude => {
7918             Require => 'QuickTime:GPSCoordinates',
7919             Groups => { 2 => 'Location' },
7920             Priority => 0, # (because it may not exist)
7921             ValueConv => 'my @c = split " ", $val; defined $c[2] ? abs($c[2]) : undef',
7922             PrintConv => '"$val m"',
7923             },
7924             GPSAltitudeRef => {
7925             Require => 'QuickTime:GPSCoordinates',
7926             Groups => { 2 => 'Location' },
7927             Priority => 0, # (because altitude information may not exist)
7928             ValueConv => 'my @c = split " ", $val; defined $c[2] ? ($c[2] < 0 ? 1 : 0) : undef',
7929             PrintConv => {
7930             0 => 'Above Sea Level',
7931             1 => 'Below Sea Level',
7932             },
7933             },
7934             GPSLatitude2 => {
7935             Name => 'GPSLatitude',
7936             Require => 'QuickTime:LocationInformation',
7937             Groups => { 2 => 'Location' },
7938             ValueConv => '$val =~ /Lat=([-+.\d]+)/; $1',
7939             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
7940             },
7941             GPSLongitude2 => {
7942             Name => 'GPSLongitude',
7943             Require => 'QuickTime:LocationInformation',
7944             Groups => { 2 => 'Location' },
7945             ValueConv => '$val =~ /Lon=([-+.\d]+)/; $1',
7946             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
7947             },
7948             GPSAltitude2 => {
7949             Name => 'GPSAltitude',
7950             Require => 'QuickTime:LocationInformation',
7951             Groups => { 2 => 'Location' },
7952             ValueConv => '$val =~ /Alt=([-+.\d]+)/; abs($1)',
7953             PrintConv => '"$val m"',
7954             },
7955             GPSAltitudeRef2 => {
7956             Name => 'GPSAltitudeRef',
7957             Require => 'QuickTime:LocationInformation',
7958             Groups => { 2 => 'Location' },
7959             ValueConv => '$val =~ /Alt=([-+.\d]+)/; $1 < 0 ? 1 : 0',
7960             PrintConv => {
7961             0 => 'Above Sea Level',
7962             1 => 'Below Sea Level',
7963             },
7964             },
7965             CDDBDiscPlayTime => {
7966             Require => 'CDDB1Info',
7967             Groups => { 2 => 'Audio' },
7968             ValueConv => '$val =~ /^..([a-z0-9]{4})/i ? hex($1) : undef',
7969             PrintConv => 'ConvertDuration($val)',
7970             },
7971             CDDBDiscTracks => {
7972             Require => 'CDDB1Info',
7973             Groups => { 2 => 'Audio' },
7974             ValueConv => '$val =~ /^.{6}([a-z0-9]{2})/i ? hex($1) : undef',
7975             },
7976             );
7977              
7978             # add our composite tags
7979             Image::ExifTool::AddCompositeTags('Image::ExifTool::QuickTime');
7980              
7981              
7982             #------------------------------------------------------------------------------
7983             # AutoLoad our routines when necessary
7984             #
7985             sub AUTOLOAD
7986             {
7987             # (Note: no need to autoload routines in QuickTimeStream that use Stream table)
7988 22 50   22   110 if ($AUTOLOAD eq 'Image::ExifTool::QuickTime::Process_mebx') {
7989 0         0 require 'Image/ExifTool/QuickTimeStream.pl';
7990 29     29   310 no strict 'refs';
  29         64  
  29         316929  
7991 0         0 return &$AUTOLOAD(@_);
7992             } else {
7993 22         123 return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
7994             }
7995             }
7996              
7997             #------------------------------------------------------------------------------
7998             # Get rotation matrix
7999             # Inputs: 0) angle in degrees
8000             # Returns: 9-element rotation matrix as a string (with 0 x/y offsets)
8001             sub GetRotationMatrix($)
8002             {
8003 1     1 0 7 my $ang = 3.1415926536 * shift() / 180;
8004 1         13 my $cos = cos $ang;
8005 1         22 my $sin = sin $ang;
8006 1         4 my $msn = -$sin;
8007 1         26 return "$cos $sin 0 $msn $cos 0 0 0 1";
8008             }
8009              
8010             #------------------------------------------------------------------------------
8011             # Get rotation angle from a matrix
8012             # Inputs: 0) rotation matrix as a string
8013             # Return: positive rotation angle in degrees rounded to 3 decimal points,
8014             # or undef on error
8015             sub GetRotationAngle($)
8016             {
8017 5     5 0 11 my $rotMatrix = shift;
8018 5         27 my @a = split ' ', $rotMatrix;
8019 5 50 66     26 return undef if $a[0]==0 and $a[1]==0;
8020             # calculate the rotation angle (assume uniform rotation)
8021 5         40 my $angle = atan2($a[1], $a[0]) * 180 / 3.14159;
8022 5 100       15 $angle += 360 if $angle < 0;
8023 5         56 return int($angle * 1000 + 0.5) / 1000;
8024             }
8025              
8026             #------------------------------------------------------------------------------
8027             # Calculate rotation of video track
8028             # Inputs: 0) ExifTool object ref
8029             # Returns: rotation angle or undef
8030             sub CalcRotation($)
8031             {
8032 5     5 0 14 my $et = shift;
8033 5         13 my $value = $$et{VALUE};
8034 5         9 my ($i, $track);
8035             # get the video track family 1 group (eg. "Track1");
8036 5         12 for ($i=0; ; ++$i) {
8037 11 100       34 my $idx = $i ? " ($i)" : '';
8038 11         23 my $tag = "HandlerType$idx";
8039 11 100       28 last unless $$value{$tag};
8040 10 100       30 next unless $$value{$tag} eq 'vide';
8041 4         16 $track = $et->GetGroup($tag, 1);
8042 4         10 last;
8043             }
8044 5 100       21 return undef unless $track;
8045             # get the video track matrix
8046 4         9 for ($i=0; ; ++$i) {
8047 12 100       59 my $idx = $i ? " ($i)" : '';
8048 12         37 my $tag = "MatrixStructure$idx";
8049 12 50       29 last unless $$value{$tag};
8050 12 100       30 next unless $et->GetGroup($tag, 1) eq $track;
8051 4         22 return GetRotationAngle($$value{$tag});
8052             }
8053 0         0 return undef;
8054             }
8055              
8056             #------------------------------------------------------------------------------
8057             # Get MatrixStructure for a given rotation angle
8058             # Inputs: 0) rotation angle (deg), 1) ExifTool ref
8059             # Returns: matrix structure as a string, or undef if it can't be rotated
8060             # - requires ImageSizeLookahead to determine the video image size, and doesn't
8061             # rotate matrix unless image size is valid
8062             sub GetMatrixStructure($$)
8063             {
8064 2     2 0 5 my ($val, $et) = @_;
8065 2         11 my @a = split ' ', $val;
8066             # pass straight through if it already has an offset
8067 2 50 33     11 return $val unless $a[6] == 0 and $a[7] == 0;
8068 2         39 my @s = split ' ', $$et{ImageSizeLookahead};
8069 2         8 my ($w, $h) = @s[12,13];
8070 2 100 66     14 return undef unless $w and $h; # don't rotate 0-sized track
8071 1         7 $_ = Image::ExifTool::QuickTime::FixWrongFormat($_) foreach $w,$h;
8072             # apply necessary offsets for the standard rotations
8073 1         5 my $angle = GetRotationAngle($val);
8074 1 50       5 return undef unless defined $angle;
8075 1 50       8 if ($angle == 90) {
    50          
    50          
8076 0         0 @a[6,7] = ($h, 0);
8077             } elsif ($angle == 180) {
8078 0         0 @a[6,7] = ($w, $h);
8079             } elsif ($angle == 270) {
8080 1         3 @a[6,7] = (0, $w);
8081             }
8082 1         6 return "@a";
8083             }
8084              
8085             #------------------------------------------------------------------------------
8086             # Determine the average sample rate from a time-to-sample table
8087             # Inputs: 0) ExifTool object ref, 1) time-to-sample table data ref
8088             # Returns: average sample rate (in Hz)
8089             sub CalcSampleRate($$)
8090             {
8091 21     21 0 70 my ($et, $valPt) = @_;
8092 21         85 my @dat = unpack('N*', $$valPt);
8093 21         57 my ($num, $dur) = (0, 0);
8094 21         30 my $i;
8095 21         83 for ($i=2; $i<@dat-1; $i+=2) {
8096 21         54 $num += $dat[$i]; # total number of samples
8097 21         80 $dur += $dat[$i] * $dat[$i+1]; # total sample duration
8098             }
8099 21 50 33     186 return undef unless $num and $dur and $$et{MediaTS};
      33        
8100 21         187 return $num * $$et{MediaTS} / $dur;
8101             }
8102              
8103             #------------------------------------------------------------------------------
8104             # Fix incorrect format for ImageWidth/Height as written by Pentax
8105             sub FixWrongFormat($)
8106             {
8107 78     78 0 138 my $val = shift;
8108 78 100       181 return undef unless $val;
8109 44 50       226 return $val & 0xfff00000 ? unpack('n',pack('N',$val)) : $val;
8110             }
8111              
8112             #------------------------------------------------------------------------------
8113             # Convert ISO 6709 string to standard lag/lon format
8114             # Inputs: 0) ISO 6709 string (lat, lon, and optional alt)
8115             # Returns: position in decimal degrees with altitude if available
8116             # Notes: Wikipedia indicates altitude may be in feet -- how is this specified?
8117             sub ConvertISO6709($)
8118             {
8119 0     0 0 0 my $val = shift;
8120 0 0       0 if ($val =~ /^([-+]\d{1,2}(?:\.\d*)?)([-+]\d{1,3}(?:\.\d*)?)([-+]\d+(?:\.\d*)?)?/) {
    0          
    0          
8121             # +DD.DDD+DDD.DDD+AA.AAA
8122 0         0 $val = ($1 + 0) . ' ' . ($2 + 0);
8123 0 0       0 $val .= ' ' . ($3 + 0) if $3;
8124             } elsif ($val =~ /^([-+])(\d{2})(\d{2}(?:\.\d*)?)([-+])(\d{3})(\d{2}(?:\.\d*)?)([-+]\d+(?:\.\d*)?)?/) {
8125             # +DDMM.MMM+DDDMM.MMM+AA.AAA
8126 0         0 my $lat = $2 + $3 / 60;
8127 0 0       0 $lat = -$lat if $1 eq '-';
8128 0         0 my $lon = $5 + $6 / 60;
8129 0 0       0 $lon = -$lon if $4 eq '-';
8130 0         0 $val = "$lat $lon";
8131 0 0       0 $val .= ' ' . ($7 + 0) if $7;
8132             } elsif ($val =~ /^([-+])(\d{2})(\d{2})(\d{2}(?:\.\d*)?)([-+])(\d{3})(\d{2})(\d{2}(?:\.\d*)?)([-+]\d+(?:\.\d*)?)?/) {
8133             # +DDMMSS.SSS+DDDMMSS.SSS+AA.AAA
8134 0         0 my $lat = $2 + $3 / 60 + $4 / 3600;
8135 0 0       0 $lat = -$lat if $1 eq '-';
8136 0         0 my $lon = $6 + $7 / 60 + $8 / 3600;
8137 0 0       0 $lon = -$lon if $5 eq '-';
8138 0         0 $val = "$lat $lon";
8139 0 0       0 $val .= ' ' . ($9 + 0) if $9;
8140             }
8141 0         0 return $val;
8142             }
8143              
8144             #------------------------------------------------------------------------------
8145             # Convert Nero chapter list (ref ffmpeg libavformat/movenc.c)
8146             # Inputs: 0) binary chpl data
8147             # Returns: chapter list
8148             sub ConvertChapterList($)
8149             {
8150 0     0 0 0 my $val = shift;
8151 0         0 my $size = length $val;
8152 0 0       0 return '' if $size < 9;
8153 0         0 my $num = Get8u(\$val, 8);
8154 0         0 my ($i, @chapters);
8155 0         0 my $pos = 9;
8156 0         0 for ($i=0; $i<$num; ++$i) {
8157 0 0       0 last if $pos + 9 > $size;
8158 0         0 my $dur = Get64u(\$val, $pos) / 10000000;
8159 0         0 my $len = Get8u(\$val, $pos + 8);
8160 0 0       0 last if $pos + 9 + $len > $size;
8161 0         0 my $title = substr($val, $pos + 9, $len);
8162 0         0 $pos += 9 + $len;
8163 0         0 push @chapters, "$dur $title";
8164             }
8165 0         0 return \@chapters; # return as a list
8166             }
8167              
8168             #------------------------------------------------------------------------------
8169             # Print conversion for a Nero chapter list item
8170             # Inputs: 0) ValueConv chapter string
8171             # Returns: formatted chapter string
8172             sub PrintChapter($)
8173             {
8174 0     0 0 0 my $val = shift;
8175 0 0       0 $val =~ /^(\S+) (.*)/ or return $val;
8176 0         0 my ($dur, $title) = ($1, $2);
8177 0         0 my $h = int($dur / 3600);
8178 0         0 $dur -= $h * 3600;
8179 0         0 my $m = int($dur / 60);
8180 0         0 my $s = $dur - $m * 60;
8181 0         0 my $ss = sprintf('%06.3f', $s);
8182 0 0       0 if ($ss >= 60) {
8183 0         0 $ss = '00.000';
8184 0 0       0 ++$m >= 60 and $m -= 60, ++$h;
8185             }
8186 0         0 return sprintf("[%d:%.2d:%s] %s",$h,$m,$ss,$title);
8187             }
8188              
8189             #------------------------------------------------------------------------------
8190             # Format GPSCoordinates for printing
8191             # Inputs: 0) string with numerical lat, lon and optional alt, separated by spaces
8192             # 1) ExifTool object reference
8193             # Returns: PrintConv value
8194             sub PrintGPSCoordinates($)
8195             {
8196 0     0 0 0 my ($val, $et) = @_;
8197 0         0 my @v = split ' ', $val;
8198 0         0 my $prt = Image::ExifTool::GPS::ToDMS($et, $v[0], 1, "N") . ', ' .
8199             Image::ExifTool::GPS::ToDMS($et, $v[1], 1, "E");
8200 0 0       0 if (defined $v[2]) {
8201 0 0       0 $prt .= ', ' . ($v[2] < 0 ? -$v[2] . ' m Below' : $v[2] . ' m Above') . ' Sea Level';
8202             }
8203 0         0 return $prt;
8204             }
8205              
8206             #------------------------------------------------------------------------------
8207             # Unpack packed ISO 639/T language code
8208             # Inputs: 0) packed language code (or undef/0), 1) true to not treat 'und' and 'eng' as default
8209             # Returns: language code, or undef/0 for default language, or 'err' for format error
8210             sub UnpackLang($;$)
8211             {
8212 10     10 0 31 my ($lang, $noDef) = @_;
8213 10 50       23 if ($lang) {
8214             # language code is packed in 5-bit characters
8215 10         18 $lang = pack 'C*', map { (($lang>>$_)&0x1f)+0x60 } 10, 5, 0;
  30         72  
8216             # validate language code
8217 10 50       40 if ($lang =~ /^[a-z]+$/) {
8218             # treat 'eng' or 'und' as the default language
8219 10 50 66     45 undef $lang if ($lang eq 'und' or $lang eq 'eng') and not $noDef;
      66        
8220             } else {
8221 0         0 $lang = 'err'; # invalid language code
8222             }
8223             }
8224 10         20 return $lang;
8225             }
8226              
8227             #------------------------------------------------------------------------------
8228             # Get language code string given QuickTime language and country codes
8229             # Inputs: 0) numerical language code, 1) numerical country code, 2) no defaults
8230             # Returns: language code string (ie. "fra-FR") or undef for default language
8231             sub GetLangCode($;$$)
8232             {
8233 6     6 0 14 my ($lang, $ctry, $noDef) = @_;
8234             # ignore country ('ctry') and language lists ('lang') for now
8235 6 50 66     19 undef $ctry if $ctry and $ctry <= 255;
8236 6 50 33     22 undef $lang if $lang and $lang <= 255;
8237 6         16 $lang = UnpackLang($lang, $noDef);
8238             # add country code if specified
8239 6 100       14 if ($ctry) {
8240 1         4 $ctry = unpack('a2',pack('n',$ctry)); # unpack as ISO 3166-1
8241             # treat 'ZZ' like a default country (see ref 12)
8242 1 50       4 undef $ctry if $ctry eq 'ZZ';
8243 1 50 33     8 if ($ctry and $ctry =~ /^[A-Z]{2}$/) {
8244 1 50       3 $lang or $lang = 'und';
8245 1         3 $lang .= "-$ctry";
8246             }
8247             }
8248 6         17 return $lang;
8249             }
8250              
8251             #------------------------------------------------------------------------------
8252             # Get langInfo hash and save details about alt-lang tags
8253             # Inputs: 0) ExifTool ref, 1) tagInfo hash ref, 2) locale code
8254             # Returns: new tagInfo hash ref, or undef if invalid
8255             sub GetLangInfoQT($$$)
8256             {
8257 1     1 0 3 my ($et, $tagInfo, $langCode) = @_;
8258 1         6 my $langInfo = Image::ExifTool::GetLangInfo($tagInfo, $langCode);
8259 1 50       4 if ($langInfo) {
8260 1 50       6 $$et{QTLang} or $$et{QTLang} = [ ];
8261 1         2 push @{$$et{QTLang}}, $$langInfo{Name};
  1         5  
8262             }
8263 1         2 return $langInfo;
8264             }
8265              
8266             #------------------------------------------------------------------------------
8267             # Get variable-length integer from data (used by ParseItemLocation)
8268             # Inputs: 0) data ref, 1) start position, 2) integer size in bytes (0, 4 or 8),
8269             # 3) default value
8270             # Returns: integer value, and updates current position
8271             sub GetVarInt($$$;$)
8272             {
8273 78     78 0 118 my ($dataPt, $pos, $n, $default) = @_;
8274 78         88 my $len = length $$dataPt;
8275 78         88 $_[1] = $pos + $n; # update current position
8276 78 50       119 return undef if $pos + $n > $len;
8277 78 50       132 if ($n == 0) {
    50          
    0          
8278 0   0     0 return $default || 0;
8279             } elsif ($n == 4) {
8280 78         136 return Get32u($dataPt, $pos);
8281             } elsif ($n == 8) {
8282 0         0 return Get64u($dataPt, $pos);
8283             }
8284 0         0 return undef;
8285             }
8286              
8287             #------------------------------------------------------------------------------
8288             # Get null-terminated string from binary data (used by ParseItemInfoEntry)
8289             # Inputs: 0) data ref, 1) start position
8290             # Returns: string, and updates current position
8291             sub GetString($$)
8292             {
8293 30     30 0 53 my ($dataPt, $pos) = @_;
8294 30         38 my $len = length $$dataPt;
8295 30         43 my $str = '';
8296 30         48 while ($pos < $len) {
8297 240         283 my $ch = substr($$dataPt, $pos, 1);
8298 240         241 ++$pos;
8299 240 100       355 last if ord($ch) == 0;
8300 215         297 $str .= $ch;
8301             }
8302 30         38 $_[1] = $pos; # update current position
8303 30         70 return $str;
8304             }
8305              
8306             #------------------------------------------------------------------------------
8307             # Get a printable version of the tag ID
8308             # Inputs: 0) tag ID, 1) Flag: 0x01 - print as 4- or 8-digit hex value if necessary
8309             # 0x02 - put leading backslash before escaped character
8310             # Returns: Printable tag ID
8311             sub PrintableTagID($;$)
8312             {
8313 22     22 0 38 my $tag = $_[0];
8314 22         57 my $n = ($tag =~ s/([\x00-\x1f\x7f-\xff])/'x'.unpack('H*',$1)/eg);
  0         0  
8315 22 0 33     62 if ($n and $_[1]) {
8316 0 0 0     0 if ($n > 2 and $_[1] & 0x01) {
    0          
8317 0         0 $tag = '0x' . unpack('H8', $_[0]);
8318 0         0 $tag =~ s/^0x0000/0x/;
8319             } elsif ($_[1] & 0x02) {
8320 0         0 ($tag = $_[0]) =~ s/([\x00-\x1f\x7f-\xff])/'\\x'.unpack('H*',$1)/eg;
  0         0  
8321             }
8322             }
8323 22         52 return $tag;
8324             }
8325              
8326             #==============================================================================
8327             # The following ParseXxx routines parse various boxes to extract this
8328             # information about embedded items in a $$et{ItemInfo} hash, keyed by item ID:
8329             #
8330             # iloc:
8331             # ConstructionMethod - offset type: 0=file, 1=idat, 2=item
8332             # DataReferenceIndex - 0 for "this file", otherwise index in dref box
8333             # BaseOffset - base for file offsets
8334             # Extents - list of details for data in file:
8335             # 0) index (extent_index)
8336             # 1) offset (extent_offset)
8337             # 2) length (extent_length)
8338             # 3) nlen (length_size)
8339             # 4) lenPt (pointer to length word)
8340             # infe:
8341             # ProtectionIndex - index if item is protected (0 for unprotected)
8342             # Name - item name
8343             # ContentType - mime type of item
8344             # ContentEncoding - item encoding
8345             # URI - URI of a 'uri '-type item
8346             # ipma:
8347             # Association - list of associated properties in the ipco container
8348             # Essential - list of "essential" flags for the associated properties
8349             # cdsc:
8350             # RefersTo - hash lookup of flags based on referred item ID
8351             # other:
8352             # DocNum - exiftool document number for this item
8353             #
8354             #------------------------------------------------------------------------------
8355             # Parse item location (iloc) box (ref ISO 14496-12:2015 pg.79)
8356             # Inputs: 0) iloc data, 1) ExifTool ref
8357             # Returns: undef, and fills in ExifTool ItemInfo hash
8358             # Notes: see also Handle_iloc() in WriteQuickTime.pl
8359             sub ParseItemLocation($$)
8360             {
8361 6     6 0 18 my ($val, $et) = @_;
8362 6         16 my ($i, $j, $num, $pos, $id);
8363 6         0 my ($extent_index, $extent_offset, $extent_length);
8364              
8365 6 100       22 my $verbose = $$et{IsWriting} ? 0 : $et->Options('Verbose');
8366 6   50     29 my $items = $$et{ItemInfo} || ($$et{ItemInfo} = { });
8367 6         10 my $len = length $val;
8368 6 50       17 return undef if $len < 8;
8369 6         16 my $ver = Get8u(\$val, 0);
8370 6         15 my $siz = Get16u(\$val, 4);
8371 6         35 my $noff = ($siz >> 12);
8372 6         13 my $nlen = ($siz >> 8) & 0x0f;
8373 6         9 my $nbas = ($siz >> 4) & 0x0f;
8374 6         11 my $nind = $siz & 0x0f;
8375 6 50       12 if ($ver < 2) {
8376 6         16 $num = Get16u(\$val, 6);
8377 6         16 $pos = 8;
8378             } else {
8379 0 0       0 return undef if $len < 10;
8380 0         0 $num = Get32u(\$val, 6);
8381 0         0 $pos = 10;
8382             }
8383 6         20 for ($i=0; $i<$num; ++$i) {
8384 20 50       30 if ($ver < 2) {
8385 20 50       33 return undef if $pos + 2 > $len;
8386 20         37 $id = Get16u(\$val, $pos);
8387 20         27 $pos += 2;
8388             } else {
8389 0 0       0 return undef if $pos + 4 > $len;
8390 0         0 $id = Get32u(\$val, $pos);
8391 0         0 $pos += 4;
8392             }
8393 20 50 33     62 if ($ver == 1 or $ver == 2) {
8394 0 0       0 return undef if $pos + 2 > $len;
8395 0         0 $$items{$id}{ConstructionMethod} = Get16u(\$val, $pos) & 0x0f;
8396 0         0 $pos += 2;
8397             }
8398 20 50       36 return undef if $pos + 2 > $len;
8399 20         34 $$items{$id}{DataReferenceIndex} = Get16u(\$val, $pos);
8400 20         26 $pos += 2;
8401 20         36 $$items{$id}{BaseOffset} = GetVarInt(\$val, $pos, $nbas);
8402 20 50       42 return undef if $pos + 2 > $len;
8403 20         60 my $ext_num = Get16u(\$val, $pos);
8404 20         22 $pos += 2;
8405 20         27 my @extents;
8406 20         36 for ($j=0; $j<$ext_num; ++$j) {
8407 20 50 33     54 if ($ver == 1 or $ver == 2) {
8408 0         0 $extent_index = GetVarInt(\$val, $pos, $nind, 1);
8409             }
8410 20         33 $extent_offset = GetVarInt(\$val, $pos, $noff);
8411 20         31 $extent_length = GetVarInt(\$val, $pos, $nlen);
8412 20 50       35 return undef unless defined $extent_length;
8413             $et->VPrint(1, "$$et{INDENT} Item $id: const_meth=",
8414             defined $$items{$id}{ConstructionMethod} ? $$items{$id}{ConstructionMethod} : '',
8415             sprintf(" base=0x%x offset=0x%x len=0x%x\n", $$items{$id}{BaseOffset},
8416 20 0       36 $extent_offset, $extent_length)) if $verbose;
    50          
8417 20         58 push @extents, [ $extent_index, $extent_offset, $extent_length, $nlen, $pos-$nlen ];
8418             }
8419             # save item location information keyed on 1-based item ID:
8420 20         52 $$items{$id}{Extents} = \@extents;
8421             }
8422 6         15 return undef;
8423             }
8424              
8425             #------------------------------------------------------------------------------
8426             # Parse content describes entry (cdsc) box
8427             # Inputs: 0) cdsc data, 1) ExifTool ref
8428             # Returns: undef, and fills in ExifTool ItemInfo hash
8429             sub ParseContentDescribes($$)
8430             {
8431 8     8 0 19 my ($val, $et) = @_;
8432 8         15 my ($id, $count, @to);
8433 8 50       19 if ($$et{ItemRefVersion}) {
8434 0 0       0 return undef if length $val < 10;
8435 0         0 ($id, $count, @to) = unpack('NnN*', $val);
8436             } else {
8437 8 50       19 return undef if length $val < 6;
8438 8         20 ($id, $count, @to) = unpack('nnn*', $val);
8439             }
8440 8 50       29 if ($count > @to) {
    50          
8441 0         0 my $str = 'Missing values in ContentDescribes box';
8442 0 0       0 $$et{IsWriting} ? $et->Error($str) : $et->Warn($str);
8443             } elsif ($count < @to) {
8444 0         0 $et->Warn('Ignored extra values in ContentDescribes box', 1);
8445 0         0 @to = $count;
8446             }
8447             # add all referenced item ID's to a "RefersTo" lookup
8448 8         35 $$et{ItemInfo}{$id}{RefersTo}{$_} = 1 foreach @to;
8449 8         21 return undef;
8450             }
8451              
8452             #------------------------------------------------------------------------------
8453             # Parse item information entry (infe) box (ref ISO 14496-12:2015 pg.82)
8454             # Inputs: 0) infe data, 1) ExifTool ref
8455             # Returns: undef, and fills in ExifTool ItemInfo hash
8456             sub ParseItemInfoEntry($$)
8457             {
8458 20     20 0 37 my ($val, $et) = @_;
8459 20         29 my $id;
8460              
8461 20 100       64 my $verbose = $$et{IsWriting} ? 0 : $et->Options('Verbose');
8462 20   50     42 my $items = $$et{ItemInfo} || ($$et{ItemInfo} = { });
8463 20         28 my $len = length $val;
8464 20 50       32 return undef if $len < 4;
8465 20         40 my $ver = Get8u(\$val, 0);
8466 20         38 my $pos = 4;
8467 20 50       41 return undef if $pos + 4 > $len;
8468 20 50 33     63 if ($ver == 0 or $ver == 1) {
8469 0         0 $id = Get16u(\$val, $pos);
8470 0         0 $$items{$id}{ProtectionIndex} = Get16u(\$val, $pos + 2);
8471 0         0 $pos += 4;
8472 0         0 $$items{$id}{Name} = GetString(\$val, $pos);
8473 0         0 $$items{$id}{ContentType} = GetString(\$val, $pos);
8474 0         0 $$items{$id}{ContentEncoding} = GetString(\$val, $pos);
8475             } else {
8476 20 50       31 if ($ver == 2) {
    0          
8477 20         42 $id = Get16u(\$val, $pos);
8478 20         33 $pos += 2;
8479             } elsif ($ver == 3) {
8480 0         0 $id = Get32u(\$val, $pos);
8481 0         0 $pos += 4;
8482             }
8483 20 50       43 return undef if $pos + 6 > $len;
8484 20         50 $$items{$id}{ProtectionIndex} = Get16u(\$val, $pos);
8485 20         39 my $type = substr($val, $pos + 2, 4);
8486 20         38 $$items{$id}{Type} = $type;
8487 20         26 $pos += 6;
8488 20         45 $$items{$id}{Name} = GetString(\$val, $pos);
8489 20 100       52 if ($type eq 'mime') {
    50          
8490 5         15 $$items{$id}{ContentType} = GetString(\$val, $pos);
8491 5         12 $$items{$id}{ContentEncoding} = GetString(\$val, $pos);
8492             } elsif ($type eq 'uri ') {
8493 0         0 $$items{$id}{URI} = GetString(\$val, $pos);
8494             }
8495             }
8496             $et->VPrint(1, "$$et{INDENT} Item $id: Type=", $$items{$id}{Type} || '',
8497             ' Name=', $$items{$id}{Name} || '',
8498 20 50 0     39 ' ContentType=', $$items{$id}{ContentType} || '',
      0        
      0        
8499             "\n") if $verbose > 1;
8500 20         36 return undef;
8501             }
8502              
8503             #------------------------------------------------------------------------------
8504             # Parse item property association (ipma) box (ref https://github.com/gpac/gpac/blob/master/src/isomedia/iff.c)
8505             # Inputs: 0) ipma data, 1) ExifTool ref
8506             # Returns: undef, and fills in ExifTool ItemInfo hash
8507             sub ParseItemPropAssoc($$)
8508             {
8509 6     6 0 16 my ($val, $et) = @_;
8510 6         10 my ($i, $j, $id);
8511              
8512 6 100       23 my $verbose = $$et{IsWriting} ? 0 : $et->Options('Verbose');
8513 6   50     19 my $items = $$et{ItemInfo} || ($$et{ItemInfo} = { });
8514 6         9 my $len = length $val;
8515 6 50       13 return undef if $len < 8;
8516 6         16 my $ver = Get8u(\$val, 0);
8517 6         15 my $flg = Get32u(\$val, 0);
8518 6         16 my $num = Get32u(\$val, 4);
8519 6         7 my $pos = 8;
8520 6         19 for ($i=0; $i<$num; ++$i) {
8521 12 50       23 if ($ver == 0) {
8522 12 50       26 return undef if $pos + 3 > $len;
8523 12         24 $id = Get16u(\$val, $pos);
8524 12         18 $pos += 2;
8525             } else {
8526 0 0       0 return undef if $pos + 5 > $len;
8527 0         0 $id = Get32u(\$val, $pos);
8528 0         0 $pos += 4;
8529             }
8530 12         25 my $n = Get8u(\$val, $pos++);
8531 12         19 my (@association, @essential);
8532 12 50       26 if ($flg & 0x01) {
8533 0 0       0 return undef if $pos + $n * 2 > $len;
8534 0         0 for ($j=0; $j<$n; ++$j) {
8535 0         0 my $tmp = Get16u(\$val, $pos + $j * 2);
8536 0         0 push @association, $tmp & 0x7fff;
8537 0 0       0 push @essential, ($tmp & 0x8000) ? 1 : 0;
8538             }
8539 0         0 $pos += $n * 2;
8540             } else {
8541 12 50       25 return undef if $pos + $n > $len;
8542 12         25 for ($j=0; $j<$n; ++$j) {
8543 24         47 my $tmp = Get8u(\$val, $pos + $j);
8544 24         41 push @association, $tmp & 0x7f;
8545 24 100       56 push @essential, ($tmp & 0x80) ? 1 : 0;
8546             }
8547 12         16 $pos += $n;
8548             }
8549 12         26 $$items{$id}{Association} = \@association;
8550 12         33 $$items{$id}{Essential} = \@essential;
8551 12 50       38 $et->VPrint(1, "$$et{INDENT} Item $id properties: @association\n") if $verbose > 1;
8552             }
8553 6         12 return undef;
8554             }
8555              
8556             #------------------------------------------------------------------------------
8557             # Process item information now
8558             # Inputs: 0) ExifTool ref
8559             sub HandleItemInfo($)
8560             {
8561 45     45 0 84 my $et = shift;
8562 45         76 my $raf = $$et{RAF};
8563 45         84 my $items = $$et{ItemInfo};
8564 45         135 my $verbose = $et->Options('Verbose');
8565 45         75 my $buff;
8566              
8567             # extract information from EXIF/XMP metadata items
8568 45 100 66     134 if ($items and $raf) {
8569 3         6 push @{$$et{PATH}}, 'ItemInformation';
  3         8  
8570 3         9 my $curPos = $raf->Tell();
8571 3         7 my $primary = $$et{PrimaryItem};
8572 3         3 my $id;
8573 3         20 $et->VerboseDir('Processing items from ItemInformation', scalar(keys %$items));
8574 3         15 foreach $id (sort { $a <=> $b } keys %$items) {
  13         26  
8575 11         18 my $item = $$items{$id};
8576 11   50     39 my $type = $$item{ContentType} || $$item{Type} || next;
8577 11 50       23 if ($verbose) {
8578             # add up total length of this item for the verbose output
8579 0         0 my $len = 0;
8580 0 0 0     0 if ($$item{Extents} and @{$$item{Extents}}) {
  0         0  
8581 0         0 $len += $$_[2] foreach @{$$item{Extents}};
  0         0  
8582             }
8583 0         0 $et->VPrint(0, "$$et{INDENT}Item $id) '${type}' ($len bytes)\n");
8584             }
8585             # get ExifTool name for this item
8586 11   100     41 my $name = { Exif => 'EXIF', 'application/rdf+xml' => 'XMP' }->{$type} || '';
8587 11         27 my ($warn, $extent);
8588 11 50       19 $warn = "Can't currently decode encoded $type metadata" if $$item{ContentEncoding};
8589 11 50       560 $warn = "Can't currently decode protected $type metadata" if $$item{ProtectionIndex};
8590 11 50       24 $warn = "Can't currently extract $type with construction method $$item{ConstructionMethod}" if $$item{ConstructionMethod};
8591 11 50 33     20 $et->WarnOnce($warn) if $warn and $name;
8592 11 50       22 $warn = 'Not this file' if $$item{DataReferenceIndex}; # (can only extract from "this file")
8593 11 50 33     20 unless (($$item{Extents} and @{$$item{Extents}}) or $warn) {
  11   33     32  
8594 0         0 $warn = "No Extents for $type item";
8595 0 0       0 $et->WarnOnce($warn) if $name;
8596             }
8597 11 50       19 if ($warn) {
8598 0 0       0 $et->VPrint(0, "$$et{INDENT} [not extracted] ($warn)\n") if $verbose > 2;
8599 0         0 next;
8600             }
8601 11   100     27 my $base = $$item{BaseOffset} || 0;
8602 11 50       19 if ($verbose > 2) {
8603             # do verbose hex dump
8604 0         0 my $len = 0;
8605 0         0 undef $buff;
8606 0         0 my $val = '';
8607 0 0       0 my $maxLen = $verbose > 3 ? 2048 : 96;
8608 0         0 foreach $extent (@{$$item{Extents}}) {
  0         0  
8609 0         0 my $n = $$extent[2];
8610 0         0 my $more = $maxLen - $len;
8611 0 0 0     0 if ($more > 0 and $n) {
8612 0 0       0 $more = $n if $more > $n;
8613 0 0       0 $val .= $buff if defined $buff;
8614 0 0       0 $raf->Seek($$extent[1] + $base, 0) or last;
8615 0 0       0 $raf->Read($buff, $more) or last;
8616             }
8617 0         0 $len += $n;
8618             }
8619 0 0       0 if (defined $buff) {
8620 0 0       0 $buff = $val . $buff if length $val;
8621 0         0 $et->VerboseDump(\$buff, DataPos => $$item{Extents}[0][1] + $base);
8622 0         0 my $snip = $len - length $buff;
8623 0 0       0 $et->VPrint(0, "$$et{INDENT} [snip $snip bytes]\n") if $snip;
8624             }
8625             }
8626 11 100       22 next unless $name;
8627             # assemble the data for this item
8628 5         9 undef $buff;
8629 5         8 my $val = '';
8630 5         7 foreach $extent (@{$$item{Extents}}) {
  5         11  
8631 5 50       11 $val .= $buff if defined $buff;
8632 5 50       19 $raf->Seek($$extent[1] + $base, 0) or last;
8633 5 100       20 $raf->Read($buff, $$extent[2]) or last;
8634             }
8635 5 50       13 next unless defined $buff;
8636 5 50       10 $buff = $val . $buff if length $val;
8637 5 100       12 next unless length $buff; # ignore empty directories
8638 4         5 my ($start, $subTable, $proc);
8639 4         11 my $pos = $$item{Extents}[0][1] + $base;
8640 4 100 66     19 if ($name eq 'EXIF' and length $buff >= 4) {
8641 2 50       17 if ($buff =~ /^(MM\0\x2a|II\x2a\0)/) {
    50          
8642 0         0 $et->Warn('Missing Exif header');
8643 0         0 $start = 0;
8644             } elsif ($buff =~ /^Exif\0\0/) {
8645             # (haven't seen this yet, but it is just a matter of time
8646             # until someone screws it up like this)
8647 0         0 $et->Warn('Missing Exif header size');
8648 0         0 $start = 6;
8649             } else {
8650 2         7 my $n = unpack('N', $buff);
8651 2         5 $start = 4 + $n; # skip "Exif\0\0" header if it exists
8652 2 50       6 if ($start > length($buff)) {
8653 0         0 $et->Warn('Invalid EXIF header');
8654 0         0 next;
8655             }
8656 2 50       5 if ($$et{HTML_DUMP}) {
8657 0         0 $et->HDump($pos, 4, 'Exif header length', "Value: $n");
8658 0 0       0 $et->HDump($pos+4, $start-4, 'Exif header') if $n;
8659             }
8660             }
8661 2         7 $subTable = GetTagTable('Image::ExifTool::Exif::Main');
8662 2         5 $proc = \&Image::ExifTool::ProcessTIFF;
8663             } else {
8664 2         5 $start = 0;
8665 2         5 $subTable = GetTagTable('Image::ExifTool::XMP::Main');
8666             }
8667 4         27 my %dirInfo = (
8668             DataPt => \$buff,
8669             DataLen => length $buff,
8670             DirStart => $start,
8671             DirLen => length($buff) - $start,
8672             DataPos => $pos,
8673             Base => $pos + $start, # (needed for HtmlDump and IsOffset tags in binary data)
8674             );
8675             # handle processing of metadata for sub-documents
8676 4 50 33     30 if (defined $primary and $$item{RefersTo} and not $$item{RefersTo}{$primary}) {
      33        
8677             # set document number if this doesn't refer to the primary document
8678 0         0 $$et{DOC_NUM} = ++$$et{DOC_COUNT};
8679             # associate this document number with the lowest item index
8680 0         0 my ($lowest) = sort { $a <=> $b } keys %{$$item{RefersTo}};
  0         0  
  0         0  
8681 0         0 $$items{$lowest}{DocNum} = $$et{DOC_NUM};
8682             }
8683 4         16 $et->ProcessDirectory(\%dirInfo, $subTable, $proc);
8684 4         14 delete $$et{DOC_NUM};
8685             }
8686 3         20 $raf->Seek($curPos, 0); # seek back to original position
8687 3         5 pop @{$$et{PATH}};
  3         10  
8688             }
8689             # process the item properties now that we should know their associations and document numbers
8690 45 100       114 if ($$et{ItemPropertyContainer}) {
8691 3         6 my ($dirInfo, $subTable, $proc) = @{$$et{ItemPropertyContainer}};
  3         7  
8692 3         10 $$et{IsItemProperty} = 1; # set item property flag
8693 3         11 $et->ProcessDirectory($dirInfo, $subTable, $proc);
8694 3         9 delete $$et{ItemPropertyContainer};
8695 3         6 delete $$et{IsItemProperty};
8696 3         16 delete $$et{DOC_NUM};
8697             }
8698 45         106 delete $$et{ItemInfo};
8699             }
8700              
8701             #------------------------------------------------------------------------------
8702             # Warn if ExtractEmbedded option isn't used
8703             # Inputs: 0) ExifTool ref
8704             sub EEWarn($)
8705             {
8706 0     0 0 0 my $et = shift;
8707 0         0 $et->WarnOnce('The ExtractEmbedded option may find more tags in the media data',3);
8708             }
8709              
8710             #------------------------------------------------------------------------------
8711             # Get quicktime format from flags word
8712             # Inputs: 0) quicktime atom flags, 1) data length
8713             # Returns: ExifTool format string
8714             sub QuickTimeFormat($$)
8715             {
8716 12     12 0 31 my ($flags, $len) = @_;
8717 12         17 my $format;
8718 12 50 33     103 if ($flags == 0x15 or $flags == 0x16) {
    50          
    50          
    50          
8719 0         0 $format = { 1=>'int8', 2=>'int16', 4=>'int32', 8=>'int64' }->{$len};
8720 0 0       0 $format .= $flags == 0x15 ? 's' : 'u' if $format;
    0          
8721             } elsif ($flags == 0x17) {
8722 0         0 $format = 'float';
8723             } elsif ($flags == 0x18) {
8724 0         0 $format = 'double';
8725             } elsif ($flags == 0x00) {
8726 0         0 $format = { 1=>'int8u', 2=>'int16u' }->{$len};
8727             }
8728 12         26 return $format;
8729             }
8730              
8731             #------------------------------------------------------------------------------
8732             # Process MPEG-4 MTDT atom (ref 11)
8733             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
8734             # Returns: 1 on success
8735             sub ProcessMetaData($$$)
8736             {
8737 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
8738 0         0 my $dataPt = $$dirInfo{DataPt};
8739 0         0 my $dirLen = length $$dataPt;
8740 0         0 my $verbose = $et->Options('Verbose');
8741 0 0       0 return 0 unless $dirLen >= 2;
8742 0         0 my $count = Get16u($dataPt, 0);
8743 0 0       0 $verbose and $et->VerboseDir('MetaData', $count);
8744 0         0 my $i;
8745 0         0 my $pos = 2;
8746 0         0 for ($i=0; $i<$count; ++$i) {
8747 0 0       0 last if $pos + 10 > $dirLen;
8748 0         0 my $size = Get16u($dataPt, $pos);
8749 0 0 0     0 last if $size < 10 or $size + $pos > $dirLen;
8750 0         0 my $tag = Get32u($dataPt, $pos + 2);
8751 0         0 my $lang = Get16u($dataPt, $pos + 6);
8752 0         0 my $enc = Get16u($dataPt, $pos + 8);
8753 0         0 my $val = substr($$dataPt, $pos + 10, $size);
8754 0         0 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
8755 0 0       0 if ($tagInfo) {
8756             # convert language code to ASCII (ignore read-only bit)
8757 0         0 $lang = UnpackLang($lang);
8758             # handle alternate languages
8759 0 0       0 if ($lang) {
8760 0         0 my $langInfo = GetLangInfoQT($et, $tagInfo, $lang);
8761 0 0       0 $tagInfo = $langInfo if $langInfo;
8762             }
8763 0 0       0 $verbose and $et->VerboseInfo($tag, $tagInfo,
8764             Value => $val,
8765             DataPt => $dataPt,
8766             Start => $pos + 10,
8767             Size => $size - 10,
8768             );
8769             # convert from UTF-16 BE if necessary
8770 0 0       0 $val = $et->Decode($val, 'UCS2') if $enc == 1;
8771 0 0 0     0 if ($enc == 0 and $$tagInfo{Unknown}) {
8772             # binary data
8773 0         0 $et->FoundTag($tagInfo, \$val);
8774             } else {
8775 0         0 $et->FoundTag($tagInfo, $val);
8776             }
8777             }
8778 0         0 $pos += $size;
8779             }
8780 0         0 return 1;
8781             }
8782              
8783             #------------------------------------------------------------------------------
8784             # Process sample description table
8785             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
8786             # Returns: 1 on success
8787             # (ref https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25691)
8788             sub ProcessSampleDesc($$$)
8789             {
8790 38     38 0 89 my ($et, $dirInfo, $tagTablePtr) = @_;
8791 38         77 my $dataPt = $$dirInfo{DataPt};
8792 38   50     146 my $pos = $$dirInfo{DirStart} || 0;
8793 38   33     100 my $dirLen = $$dirInfo{DirLen} || (length($$dataPt) - $pos);
8794 38 50       97 return 0 if $pos + 8 > $dirLen;
8795              
8796 38         107 my $num = Get32u($dataPt, 4); # get number of sample entries in table
8797 38         82 $pos += 8;
8798 38         59 my ($i, $err);
8799 38         124 for ($i=0; $i<$num; ++$i) { # loop through sample entries
8800 38 50       104 $pos + 8 > $dirLen and $err = 1, last;
8801 38         102 my $size = Get32u($dataPt, $pos);
8802 38 50       104 $pos + $size > $dirLen and $err = 1, last;
8803 38         99 $$dirInfo{DirStart} = $pos;
8804 38         73 $$dirInfo{DirLen} = $size;
8805 38         132 ProcessHybrid($et, $dirInfo, $tagTablePtr);
8806 38         108 $pos += $size;
8807             }
8808 38 0 33     89 if ($err and $$et{HandlerType}) {
8809 0   0     0 my $grp = $$et{SET_GROUP1} || $$dirInfo{Parent} || 'unknown';
8810 0         0 $et->Warn("Truncated $$et{HandlerType} sample table for $grp");
8811             }
8812 38         102 return 1;
8813             }
8814              
8815             #------------------------------------------------------------------------------
8816             # Process hybrid binary data + QuickTime container (ref PH)
8817             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
8818             # Returns: 1 on success
8819             sub ProcessHybrid($$$)
8820             {
8821 38     38 0 90 my ($et, $dirInfo, $tagTablePtr) = @_;
8822             # brute-force search for child atoms after first 8 bytes of binary data
8823 38         79 my $dataPt = $$dirInfo{DataPt};
8824 38   50     104 my $dirStart = $$dirInfo{DirStart} || 0;
8825 38   33     99 my $dirLen = $$dirInfo{DirLen} || length($$dataPt) - $dirStart;
8826 38         62 my $end = $dirStart + $dirLen;
8827 38         68 my $pos = $dirStart + 8; # skip length/version
8828 38         60 my $try = $pos;
8829 38         52 my $childPos;
8830              
8831 38         95 while ($pos <= $end - 8) {
8832 2204         3005 my $tag = substr($$dataPt, $try+4, 4);
8833             # look only for well-behaved tag ID's
8834 2204 100       4848 $tag =~ /[^\w ]/ and $try = ++$pos, next;
8835 94         181 my $size = Get32u($dataPt, $try);
8836 94 100       197 if ($size + $try == $end) {
8837             # the atom ends exactly at the end of the parent -- this must be it
8838 16         38 $childPos = $pos;
8839 16         37 $$dirInfo{DirLen} = $pos; # the binary data ends at the first child atom
8840 16         35 last;
8841             }
8842 78 100 100     255 if ($size < 8 or $size + $try > $end - 8) {
8843 58         109 $try = ++$pos; # fail. try next position
8844             } else {
8845 20         46 $try += $size; # could be another atom following this
8846             }
8847             }
8848             # process binary data
8849 38         106 $$dirInfo{MixedTags} = 1; # ignore non-integer tag ID's
8850 38         139 $et->ProcessBinaryData($dirInfo, $tagTablePtr);
8851             # process child atoms if found
8852 38 100       121 if ($childPos) {
8853 16         40 $$dirInfo{DirStart} = $childPos;
8854 16         36 $$dirInfo{DirLen} = $end - $childPos;
8855 16         58 ProcessMOV($et, $dirInfo, $tagTablePtr);
8856             }
8857 38         89 return 1;
8858             }
8859              
8860             #------------------------------------------------------------------------------
8861             # Process iTunes 'righ' atom (ref PH)
8862             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
8863             # Returns: 1 on success
8864             sub ProcessRights($$$)
8865             {
8866 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
8867 0         0 my $dataPt = $$dirInfo{DataPt};
8868 0         0 my $dataPos = $$dirInfo{Base};
8869 0         0 my $dirLen = length $$dataPt;
8870 0   0     0 my $unknown = $$et{OPTIONS}{Unknown} || $$et{OPTIONS}{Verbose};
8871 0         0 my $pos;
8872 0         0 $et->VerboseDir('righ', $dirLen / 8);
8873 0         0 for ($pos = 0; $pos + 8 <= $dirLen; $pos += 8) {
8874 0         0 my $tag = substr($$dataPt, $pos, 4);
8875 0 0       0 last if $tag eq "\0\0\0\0";
8876 0         0 my $val = substr($$dataPt, $pos + 4, 4);
8877 0         0 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
8878 0 0       0 unless ($tagInfo) {
8879 0 0       0 next unless $unknown;
8880 0         0 my $name = PrintableTagID($tag);
8881 0         0 $tagInfo = {
8882             Name => "Unknown_$name",
8883             Description => "Unknown $name",
8884             Unknown => 1,
8885             },
8886             AddTagToTable($tagTablePtr, $tag, $tagInfo);
8887             }
8888 0 0       0 $val = '0x' . unpack('H*', $val) unless $$tagInfo{Format};
8889 0         0 $et->HandleTag($tagTablePtr, $tag, $val,
8890             DataPt => $dataPt,
8891             DataPos => $dataPos,
8892             Start => $pos + 4,
8893             Size => 4,
8894             );
8895             }
8896 0         0 return 1;
8897             }
8898              
8899             #------------------------------------------------------------------------------
8900             # Process iTunes Encoding Params (ref PH)
8901             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
8902             # Returns: 1 on success
8903             sub ProcessEncodingParams($$$)
8904             {
8905 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
8906 0         0 my $dataPt = $$dirInfo{DataPt};
8907 0         0 my $dirLen = length $$dataPt;
8908 0         0 my $pos;
8909 0         0 $et->VerboseDir('Encoding Params', $dirLen / 8);
8910 0         0 for ($pos = 0; $pos + 8 <= $dirLen; $pos += 8) {
8911 0         0 my ($tag, $val) = unpack("x${pos}a4N", $$dataPt);
8912 0         0 $et->HandleTag($tagTablePtr, $tag, $val);
8913             }
8914 0         0 return 1;
8915             }
8916              
8917             #------------------------------------------------------------------------------
8918             # Read Meta Keys and add tags to ItemList table ('mdta' handler) (ref PH)
8919             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
8920             # Returns: 1 on success
8921             sub ProcessKeys($$$)
8922             {
8923 9     9 0 18 local $_;
8924 9         25 my ($et, $dirInfo, $tagTablePtr) = @_;
8925 9         21 my $dataPt = $$dirInfo{DataPt};
8926 9         17 my $dirLen = length $$dataPt;
8927 9         11 my $out;
8928 9 50       32 if ($et->Options('Verbose')) {
8929 0         0 $et->VerboseDir('Keys');
8930 0         0 $out = $et->Options('TextOut');
8931             }
8932 9         21 my $pos = 8;
8933 9         17 my $index = 1;
8934 9         19 ++$$et{KeysCount}; # increment key count for this directory
8935 9         23 my $itemList = GetTagTable('Image::ExifTool::QuickTime::ItemList');
8936 9         33 my $userData = GetTagTable('Image::ExifTool::QuickTime::UserData');
8937 9         43 while ($pos < $dirLen - 4) {
8938 22         70 my $len = unpack("x${pos}N", $$dataPt);
8939 22 50 33     104 last if $len < 8 or $pos + $len > $dirLen;
8940 22         42 delete $$tagTablePtr{$index};
8941 22         55 my $ns = substr($$dataPt, $pos + 4, 4);
8942 22         46 my $tag = substr($$dataPt, $pos + 8, $len - 8);
8943 22         52 $tag =~ s/\0.*//s; # truncate at null
8944 22         32 my $full = $tag;
8945 22 50       114 $tag =~ s/^com\.(apple\.quicktime\.)?// if $ns eq 'mdta'; # remove apple quicktime domain
8946 22 50       51 $tag = "Tag_$ns" unless $tag;
8947 22         38 my $short = $tag;
8948 22         26 my $tagInfo;
8949 22         31 for (;;) {
8950 22 50       51 $tagInfo = $et->GetTagInfo($tagTablePtr, $tag) and last;
8951             # also try ItemList and UserData tables
8952 0 0       0 $tagInfo = $et->GetTagInfo($itemList, $tag) and last;
8953 0 0       0 $tagInfo = $et->GetTagInfo($userData, $tag) and last;
8954             # (I have some samples where the tag is a reversed ItemList or UserData tag ID)
8955 0 0       0 if ($tag =~ /^\w{3}\xa9$/) {
8956 0         0 $tag = pack('N', unpack('V', $tag));
8957 0 0       0 $tagInfo = $et->GetTagInfo($itemList, $tag) and last;
8958 0         0 $tagInfo = $et->GetTagInfo($userData, $tag);
8959 0         0 last;
8960             }
8961 0 0       0 if ($tag eq $full) {
8962 0         0 $tag = $short;
8963 0         0 last;
8964             }
8965 0         0 $tag = $full;
8966             }
8967 22         50 my ($newInfo, $msg);
8968 22 50 0     61 if ($tagInfo) {
    0          
8969             # copy tag information into new Keys tag
8970             $newInfo = {
8971             Name => $$tagInfo{Name},
8972             Format => $$tagInfo{Format},
8973             ValueConv => $$tagInfo{ValueConv},
8974             ValueConvInv => $$tagInfo{ValueConvInv},
8975             PrintConv => $$tagInfo{PrintConv},
8976             PrintConvInv => $$tagInfo{PrintConvInv},
8977             Writable => defined $$tagInfo{Writable} ? $$tagInfo{Writable} : 1,
8978             SubDirectory => $$tagInfo{SubDirectory},
8979 22 50       189 };
8980 22         46 my $groups = $$tagInfo{Groups};
8981 22 100       100 $$newInfo{Groups} = $groups ? { %$groups } : { };
8982 22   66     162 $$newInfo{Groups}{$_} or $$newInfo{Groups}{$_} = $$tagTablePtr{GROUPS}{$_} foreach 0..2;
8983 22         51 $$newInfo{Groups}{1} = 'Keys';
8984             } elsif ($tag =~ /^[-\w. ]+$/ or $tag =~ /\w{4}/) {
8985             # create info for tags with reasonable id's
8986 0         0 my $name = ucfirst $tag;
8987 0         0 $name =~ tr/-0-9a-zA-Z_. //dc;
8988 0         0 $name =~ s/[. ]+(.?)/\U$1/g;
8989 0         0 $name =~ s/_([a-z])/_\U$1/g;
8990 0         0 $name =~ s/([a-z])_([A-Z])/$1$2/g;
8991 0 0       0 $name = "Tag_$name" if length $name < 2;
8992 0         0 $newInfo = { Name => $name, Groups => { 1 => 'Keys' } };
8993 0         0 $msg = ' (Unknown)';
8994             }
8995             # substitute this tag in the ItemList table with the given index
8996 22         57 my $id = $$et{KeysCount} . '.' . $index;
8997 22 100       69 if (ref $$itemList{$id} eq 'HASH') {
8998             # delete other languages too if they exist
8999 18         37 my $oldInfo = $$itemList{$id};
9000 18 50       49 if ($$oldInfo{OtherLang}) {
9001 0         0 delete $$itemList{$_} foreach @{$$oldInfo{OtherLang}};
  0         0  
9002             }
9003 18         119 delete $$itemList{$id};
9004             }
9005 22 50       42 if ($newInfo) {
9006 22         47 $$newInfo{KeysID} = $tag; # save original ID for use in family 7 group name
9007 22         61 AddTagToTable($itemList, $id, $newInfo);
9008 22 50       41 $msg or $msg = '';
9009 22 50       47 $out and print $out "$$et{INDENT}Added ItemList Tag $id = ($ns) $tag$msg\n";
9010             }
9011 22         35 $pos += $len;
9012 22         64 ++$index;
9013             }
9014 9         26 return 1;
9015             }
9016              
9017             #------------------------------------------------------------------------------
9018             # Process keys in MetaSampleDesc directory
9019             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9020             # Returns: 1 on success
9021             sub ProcessMetaKeys($$$)
9022             {
9023 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
9024             # save this information to decode timed metadata samples when ExtractEmbedded is used
9025 0 0       0 SaveMetaKeys($et, $dirInfo, $tagTablePtr) if $$et{OPTIONS}{ExtractEmbedded};
9026 0         0 return 1;
9027             }
9028              
9029             #------------------------------------------------------------------------------
9030             # Process a QuickTime atom
9031             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) optional tag table ref
9032             # Returns: 1 on success
9033             sub ProcessMOV($$;$)
9034             {
9035 383     383 0 573 local $_;
9036 383         673 my ($et, $dirInfo, $tagTablePtr) = @_;
9037 383         614 my $raf = $$dirInfo{RAF};
9038 383         577 my $dataPt = $$dirInfo{DataPt};
9039 383         988 my $verbose = $et->Options('Verbose');
9040 383         725 my $validate = $$et{OPTIONS}{Validate};
9041 383   100     823 my $dataPos = $$dirInfo{Base} || 0;
9042 383   100     851 my $dirID = $$dirInfo{DirID} || '';
9043 383         690 my $charsetQuickTime = $et->Options('CharsetQuickTime');
9044 383         934 my ($buff, $tag, $size, $track, $isUserData, %triplet, $doDefaultLang, $index);
9045 383         0 my ($dirEnd, $unkOpt, %saveOptions, $atomCount);
9046              
9047 383         618 my $topLevel = not $$et{InQuickTime};
9048 383         551 $$et{InQuickTime} = 1;
9049 383 100       829 $$et{HandlerType} = $$et{MetaFormat} = '' unless defined $$et{HandlerType};
9050              
9051 383 100       715 unless (defined $$et{KeysCount}) {
9052 20         49 $$et{KeysCount} = 0; # initialize ItemList key directory count
9053 20         42 $doDefaultLang = 1; # flag to generate default language tags
9054             }
9055             # more convenient to package data as a RandomAccess file
9056 383 100       730 unless ($raf) {
9057 363         998 $raf = new File::RandomAccess($dataPt);
9058 363 50 100     1405 $dirEnd = $dataPos + $$dirInfo{DirLen} + ($$dirInfo{DirStart} || 0) if $$dirInfo{DirLen};
9059             }
9060             # skip leading bytes if necessary
9061 383 100       773 if ($$dirInfo{DirStart}) {
9062 94 50       296 $raf->Seek($$dirInfo{DirStart}, 1) or return 0;
9063 94         172 $dataPos += $$dirInfo{DirStart};
9064             }
9065             # read size/tag name atom header
9066 383 50       799 $raf->Read($buff,8) == 8 or return 0;
9067 383         582 $dataPos += 8;
9068 383 100       614 if ($tagTablePtr) {
9069 363         919 $isUserData = ($tagTablePtr eq \%Image::ExifTool::QuickTime::UserData);
9070             } else {
9071 20         60 $tagTablePtr = GetTagTable('Image::ExifTool::QuickTime::Main');
9072             }
9073 383         1299 ($size, $tag) = unpack('Na4', $buff);
9074 383 100       753 if ($dataPt) {
9075 363 50       685 $verbose and $et->VerboseDir($$dirInfo{DirName});
9076             } else {
9077             # check on file type if called with a RAF
9078 20 50       73 $$tagTablePtr{$tag} or return 0;
9079 20 100 66     100 if ($tag eq 'ftyp' and $size >= 12) {
9080             # read ftyp atom to see what type of file this is
9081 11         27 my $fileType;
9082 11 50       40 if ($raf->Read($buff, $size-8) == $size-8) {
9083 11         45 $raf->Seek(-($size-8), 1);
9084 11         45 my $type = substr($buff, 0, 4);
9085 11         38 $$et{save_ftyp} = $type;
9086             # see if we know the extension for this file type
9087 11 50 33     124 if ($ftypLookup{$type} and $ftypLookup{$type} =~ /\(\.(\w+)/) {
    0          
    0          
    0          
9088 11         44 $fileType = $1;
9089             # check compatible brands
9090             } elsif ($buff =~ /^.{8}(.{4})+(mp41|mp42|avc1)/s) {
9091 0         0 $fileType = 'MP4';
9092             } elsif ($buff =~ /^.{8}(.{4})+(f4v )/s) {
9093 0         0 $fileType = 'F4V';
9094             } elsif ($buff =~ /^.{8}(.{4})+(qt )/s) {
9095 0         0 $fileType = 'MOV';
9096             }
9097             }
9098 11 50       37 $fileType or $fileType = 'MP4'; # default to MP4
9099 11   50     80 $et->SetFileType($fileType, $mimeLookup{$fileType} || 'video/mp4');
9100             # temporarily set ExtractEmbedded option for CRX files
9101 11 100       54 $saveOptions{ExtractEmbedded} = $et->Options(ExtractEmbedded => 1) if $fileType eq 'CRX';
9102             } else {
9103 9         37 $et->SetFileType(); # MOV
9104             }
9105 20         78 SetByteOrder('MM');
9106 20         56 $$et{PRIORITY_DIR} = 'XMP'; # have XMP take priority
9107             }
9108 383 50       915 $$raf{NoBuffer} = 1 if $et->Options('FastScan'); # disable buffering in FastScan mode
9109              
9110 383         710 my $ee = $$et{OPTIONS}{ExtractEmbedded};
9111 383 100       639 if ($ee) {
9112 134         261 $unkOpt = $$et{OPTIONS}{Unknown};
9113 134         2436 require 'Image/ExifTool/QuickTimeStream.pl';
9114             }
9115 383 100       869 if ($$tagTablePtr{VARS}) {
9116 22         51 $index = $$tagTablePtr{VARS}{START_INDEX};
9117 22         45 $atomCount = $$tagTablePtr{VARS}{ATOM_COUNT};
9118             }
9119 383         446 for (;;) {
9120 1253         1782 my ($eeTag, $ignore);
9121 1253 50 33     2327 last if defined $atomCount and --$atomCount < 0;
9122 1253 100       1974 if ($size < 8) {
9123 4 50       16 if ($size == 0) {
9124 0 0       0 if ($dataPt) {
9125             # a zero size isn't legal for contained atoms, but Canon uses it to
9126             # terminate the CNTH atom (eg. CanonEOS100D.mov), so tolerate it here
9127 0         0 my $pos = $raf->Tell() - 4;
9128 0         0 $raf->Seek(0,2);
9129 0         0 my $str = $$dirInfo{DirName} . ' with ' . ($raf->Tell() - $pos) . ' bytes';
9130 0         0 $et->VPrint(0,"$$et{INDENT}\[Terminator found in $str remaining]");
9131             } else {
9132 0         0 my $t = PrintableTagID($tag,2);
9133 0         0 $et->VPrint(0,"$$et{INDENT}Tag '${t}' extends to end of file");
9134 0 0       0 if ($$tagTablePtr{"$tag-size"}) {
9135 0         0 my $pos = $raf->Tell();
9136 0         0 $raf->Seek(0, 2);
9137 0         0 $et->HandleTag($tagTablePtr, "$tag-size", $raf->Tell() - $pos);
9138 0 0       0 $et->HandleTag($tagTablePtr, "$tag-offset", $pos) if $$tagTablePtr{"$tag-offset"};
9139             }
9140             }
9141 0         0 last;
9142             }
9143 4 50       15 $size == 1 or $et->Warn('Invalid atom size'), last;
9144             # read extended atom size
9145 4 50       13 $raf->Read($buff, 8) == 8 or $et->Warn('Truncated atom header'), last;
9146 4         9 $dataPos += 8;
9147 4         14 my ($hi, $lo) = unpack('NN', $buff);
9148 4 50 33     110 if ($hi or $lo > 0x7fffffff) {
9149 0 0       0 if ($hi > 0x7fffffff) {
    0          
9150 0         0 $et->Warn('Invalid atom size');
9151 0         0 last;
9152             } elsif (not $et->Options('LargeFileSupport')) {
9153 0         0 $et->Warn('End of processing at large atom (LargeFileSupport not enabled)');
9154 0         0 last;
9155             }
9156             }
9157 4         13 $size = $hi * 4294967296 + $lo - 16;
9158 4 50       15 $size < 0 and $et->Warn('Invalid extended size'), last;
9159             } else {
9160 1249         1613 $size -= 8;
9161             }
9162 1253 50       2040 if ($validate) {
9163 0 0       0 $$et{ValidatePath} or $$et{ValidatePath} = { };
9164 0         0 my $path = join('-', @{$$et{PATH}}, $tag);
  0         0  
9165 0 0       0 $path =~ s/-Track-/-$$et{SET_GROUP1}-/ if $$et{SET_GROUP1};
9166 0 0 0     0 if ($$et{ValidatePath}{$path} and not $dupTagOK{$tag} and not $dupDirOK{$dirID}) {
      0        
9167 0         0 my $i = Get32u(\$tag,0);
9168 0 0       0 my $str = $i < 255 ? "index $i" : "tag '" . PrintableTagID($tag,2) . "'";
9169 0         0 $et->WarnOnce("Duplicate $str at " . join('-', @{$$et{PATH}}));
  0         0  
9170 0 0       0 $$et{ValidatePath} = { } if $path eq 'MOV-moov'; # avoid warnings for all contained dups
9171             }
9172 0         0 $$et{ValidatePath}{$path} = 1;
9173             }
9174 1253 50 66     2231 if ($isUserData and $$et{SET_GROUP1}) {
9175 0         0 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
9176             # add track name to UserData tags inside tracks
9177 0         0 $tag = $$et{SET_GROUP1} . $tag;
9178 0 0 0     0 if (not $$tagTablePtr{$tag} and $tagInfo) {
9179 0         0 my %newInfo = %$tagInfo;
9180 0         0 foreach ('Name', 'Description') {
9181 0 0       0 next unless $$tagInfo{$_};
9182 0         0 $newInfo{$_} = $$et{SET_GROUP1} . $$tagInfo{$_};
9183 0         0 $newInfo{$_} =~ s/^(Track\d+)Track/$1/; # remove duplicate "Track" in name
9184             }
9185 0         0 AddTagToTable($tagTablePtr, $tag, \%newInfo);
9186             }
9187             }
9188             # set flag to store additional information for ExtractEmbedded option
9189 1253         2372 my $handlerType = $$et{HandlerType};
9190 1253 100 100     5281 if ($eeBox{$handlerType} and $eeBox{$handlerType}{$tag}) {
    50 66        
      33        
      0        
9191 104 100 33     268 if ($ee) {
    50          
9192             # (there is another 'gps ' box with a track log that doesn't contain offsets)
9193 68 50 33     161 if ($tag ne 'gps ' or $eeBox{$handlerType}{$tag} eq $dirID) {
9194 68         83 $eeTag = 1;
9195 68         121 $$et{OPTIONS}{Unknown} = 1; # temporarily enable "Unknown" option
9196             }
9197             } elsif ($handlerType ne 'vide' and not $$et{OPTIONS}{Validate}) {
9198 0         0 EEWarn($et);
9199             }
9200             } elsif ($ee and $ee > 1 and $eeBox2{$handlerType} and $eeBox2{$handlerType}{$tag}) {
9201 0         0 $eeTag = 1;
9202 0         0 $$et{OPTIONS}{Unknown} = 1;
9203             }
9204 1253         2878 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
9205              
9206 1253 100       2585 $$et{OPTIONS}{Unknown} = $unkOpt if $eeTag; # restore Unknown option
9207              
9208             # allow numerical tag ID's
9209 1253 100       2152 unless ($tagInfo) {
9210 160         538 my $id = $$et{KeysCount} . '.' . unpack('N', $tag);
9211 160 100       422 if ($$tagTablePtr{$id}) {
9212 22         54 $tagInfo = $et->GetTagInfo($tagTablePtr, $id);
9213 22         47 $tag = $id;
9214             }
9215             }
9216             # generate tagInfo if Unknown option set
9217 1253 50 66     2596 if (not defined $tagInfo and ($$et{OPTIONS}{Unknown} or
      66        
9218             $verbose or $tag =~ /^\xa9/))
9219             {
9220 13         39 my $name = PrintableTagID($tag,1);
9221 13 50       43 if ($name =~ /^xa9(.*)/) {
9222 0         0 $tagInfo = {
9223             Name => "UserData_$1",
9224             Description => "User Data $1",
9225             };
9226             } else {
9227 13         86 $tagInfo = {
9228             Name => "Unknown_$name",
9229             Description => "Unknown $name",
9230             %unknownInfo,
9231             };
9232             }
9233 13         44 AddTagToTable($tagTablePtr, $tag, $tagInfo);
9234             }
9235             # save required tag sizes
9236 1253 100       3039 if ($$tagTablePtr{"$tag-size"}) {
9237 23         119 $et->HandleTag($tagTablePtr, "$tag-size", $size);
9238 23 50       172 $et->HandleTag($tagTablePtr, "$tag-offset", $raf->Tell()) if $$tagTablePtr{"$tag-offset"};
9239             }
9240             # load values only if associated with a tag (or verbose) and not too big
9241 1253 50       2149 if ($size > 0x2000000) { # start to get worried above 32 MB
9242             # check for RIFF trailer (written by Auto-Vox dashcam)
9243 0 0       0 if ($buff =~ /^(gpsa|gps0|gsen|gsea)...\0/s) { # (yet seen only gpsa as first record)
9244 0         0 $et->VPrint(0, "Found RIFF trailer");
9245 0 0       0 if ($et->Options('ExtractEmbedded')) {
9246 0 0       0 $raf->Seek(-8, 1) or last; # seek back to start of trailer
9247 0         0 my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
9248 0         0 ProcessRIFFTrailer($et, { RAF => $raf }, $tbl);
9249             } else {
9250 0         0 EEWarn($et);
9251             }
9252 0         0 last;
9253             }
9254 0         0 $ignore = 1;
9255 0 0 0     0 if ($tagInfo and not $$tagInfo{Unknown} and not $eeTag) {
      0        
9256 0         0 my $t = PrintableTagID($tag,2);
9257 0 0       0 if ($size > 0x8000000) {
9258 0         0 $et->Warn("Skipping '${t}' atom > 128 MB", 1);
9259             } else {
9260 0 0       0 $et->Warn("Skipping '${t}' atom > 32 MB", 2) or $ignore = 0;
9261             }
9262             }
9263             }
9264 1253 100 66     3208 if (defined $tagInfo and not $ignore) {
9265             # set document number for this item property if necessary
9266 1165 100       2184 if ($$et{IsItemProperty}) {
9267 12         19 my $items = $$et{ItemInfo};
9268 12         19 my ($id, $prop, $docNum, $lowest);
9269 12   50     27 my $primary = $$et{PrimaryItem} || 0;
9270 12         33 ItemID: foreach $id (keys %$items) {
9271 34 100       66 next unless $$items{$id}{Association};
9272 20         28 my $item = $$items{$id};
9273 20         26 foreach $prop (@{$$item{Association}}) {
  20         33  
9274 37 100       63 next unless $prop == $index;
9275 12 100 0     66 if ($id == $primary or (not $dontInherit{$tag} and
    100 33        
    50 66        
      33        
9276             (not $$item{RefersTo} or $$item{RefersTo}{$primary})))
9277             {
9278             # this is associated with the primary item or an item describing
9279             # the primary item, so consider this part of the main document
9280 6         7 undef $docNum;
9281 6         9 undef $lowest;
9282 6         11 last ItemID;
9283             } elsif ($$item{DocNum}) {
9284             # this property is already associated with an item that has
9285             # an ExifTool document number, so use the lowest associated DocNum
9286 3 50 33     14 $docNum = $$item{DocNum} if not defined $docNum or $docNum > $$item{DocNum};
9287             } elsif (not defined $lowest or $lowest > $id) {
9288             # keep track of the lowest associated item ID
9289 3         7 $lowest = $id;
9290             }
9291             }
9292             }
9293 12 100 100     41 if (not defined $docNum and defined $lowest) {
9294             # this is the first time we've seen metadata from this item,
9295             # so use a new document number
9296 3         4 $docNum = ++$$et{DOC_COUNT};
9297 3         6 $$items{$lowest}{DocNum} = $docNum;
9298             }
9299 12         24 $$et{DOC_NUM} = $docNum;
9300             }
9301 1165         1384 my $val;
9302 1165         2439 my $missing = $size - $raf->Read($val, $size);
9303 1165 50       2059 if ($missing) {
9304 0         0 my $t = PrintableTagID($tag,2);
9305 0         0 $et->Warn("Truncated '${t}' data (missing $missing bytes)");
9306 0         0 last;
9307             }
9308             # use value to get tag info if necessary
9309 1165 100       2052 $tagInfo or $tagInfo = $et->GetTagInfo($tagTablePtr, $tag, \$val);
9310 1165   66     2881 my $hasData = ($$dirInfo{HasData} and $val =~ /\0...data\0/s);
9311 1165 50 33     2271 if ($verbose and not $hasData) {
9312 0         0 my $tval;
9313 0 0 0     0 if ($tagInfo and $$tagInfo{Format}) {
9314 0         0 $tval = ReadValue(\$val, 0, $$tagInfo{Format}, $$tagInfo{Count}, length($val));
9315             }
9316             $et->VerboseInfo($tag, $tagInfo,
9317             Value => $tval,
9318             DataPt => \$val,
9319             DataPos => $dataPos,
9320             Size => $size,
9321             Format => $tagInfo ? $$tagInfo{Format} : undef,
9322 0 0       0 Index => $index,
9323             );
9324             # print iref item ID numbers
9325 0 0       0 if ($dirID eq 'iref') {
9326 0         0 my ($id, $count, @to, $i);
9327 0 0       0 if ($$et{ItemRefVersion}) {
9328 0 0       0 ($id, $count, @to) = unpack('NnN*', $val) if length $val >= 10;
9329             } else {
9330 0 0       0 ($id, $count, @to) = unpack('nnn*', $val) if length $val >= 6;
9331             }
9332 0 0       0 defined $id or $id = '', $count = 0;
9333 0 0       0 $id .= " (wrong count: $count)" if $count != @to;
9334             # convert sequential numbers to a range
9335 0         0 for ($i=1; $i<@to; ) {
9336 0 0 0     0 $to[$i-1] =~ /(\d+)$/ and $to[$i] == $1 + 1 or ++$i, next;
9337 0         0 $to[$i-1] =~ s/(-.*)?$/-$to[$i]/;
9338 0         0 splice @to, $i, 1;
9339             }
9340 0         0 $et->VPrint(1, "$$et{INDENT} Item $id refers to: ",join(',',@to),"\n");
9341             }
9342             }
9343             # extract metadata from stream if ExtractEmbedded option is enabled
9344 1165 100       1961 if ($eeTag) {
9345 68         220 ParseTag($et, $tag, \$val);
9346             # forget this tag if we generated it only for ExtractEmbedded
9347 68 100 66     320 undef $tagInfo if $tagInfo and $$tagInfo{Unknown} and not $unkOpt;
      100        
9348             }
9349              
9350             # handle iTunesInfo mean/name/data triplets
9351 1165 100 100     3533 if ($tagInfo and $$tagInfo{Triplet}) {
9352 9 100 66     44 if ($tag eq 'data' and $triplet{mean} and $triplet{name}) {
      33        
9353 3         7 $tag = $triplet{name};
9354             # add 'mean' to name unless it is 'com.apple.iTunes'
9355 3 50       12 $tag = $triplet{mean} . '/' . $tag unless $triplet{mean} eq 'com.apple.iTunes';
9356 3         9 $tagInfo = $et->GetTagInfo($tagTablePtr, $tag, \$val);
9357 3 50       11 unless ($tagInfo) {
9358 0         0 my $name = $triplet{name};
9359 0         0 my $desc = $name;
9360 0         0 $name =~ tr/-_a-zA-Z0-9//dc;
9361 0         0 $desc =~ tr/_/ /;
9362 0         0 $tagInfo = {
9363             Name => $name,
9364             Description => $desc,
9365             };
9366 0         0 $et->VPrint(0, $$et{INDENT}, "[adding QuickTime:$name]\n");
9367 0         0 AddTagToTable($tagTablePtr, $tag, $tagInfo);
9368             }
9369             # ignore 8-byte header
9370 3 50       13 $val = substr($val, 8) if length($val) >= 8;
9371 3 50 33     18 unless ($$tagInfo{Format} or $$tagInfo{SubDirectory}) {
9372             # extract as binary if it contains any non-ASCII or control characters
9373 3 50       17 if ($val =~ /[^\x20-\x7e]/) {
9374 0         0 my $buff = $val;
9375 0         0 $val = \$buff;
9376             }
9377             }
9378 3         11 $$tagInfo{List} = 1; # (allow any of these tags to have multiple data elements)
9379 3 50       61 $et->VerboseInfo($tag, $tagInfo, Value => $val) if $verbose;
9380             } else {
9381 6 50       25 $triplet{$tag} = substr($val,4) if length($val) > 4;
9382 6         9 undef $tagInfo; # don't store this tag
9383             }
9384             }
9385 1165 100       1888 if ($tagInfo) {
9386 1131         1641 my $subdir = $$tagInfo{SubDirectory};
9387 1131 100 66     2763 if ($subdir) {
    100          
    100          
9388 674   100     1844 my $start = $$subdir{Start} || 0;
9389 674         1111 my ($base, $dPos) = ($dataPos, 0);
9390 674 50       1324 if ($$subdir{Base}) {
9391 0         0 $dPos -= eval $$subdir{Base};
9392 0         0 $base -= $dPos;
9393             }
9394             my %dirInfo = (
9395             DataPt => \$val,
9396             DataLen => $size,
9397             DirStart => $start,
9398             DirLen => $size - $start,
9399             DirName => $$subdir{DirName} || $$tagInfo{Name},
9400             DirID => $tag,
9401             HasData => $$subdir{HasData},
9402             Multi => $$subdir{Multi},
9403             IgnoreProp => $$subdir{IgnoreProp}, # (XML hack)
9404 674   66     4932 DataPos => $dPos,
9405             Base => $base, # (needed for IsOffset tags in binary data)
9406             );
9407 674 50       1395 $dirInfo{BlockInfo} = $tagInfo if $$tagInfo{BlockExtract};
9408 674 100 66     1423 if ($$subdir{ByteOrder} and $$subdir{ByteOrder} =~ /^Little/) {
9409 6         43 SetByteOrder('II');
9410             }
9411 674         997 my $oldGroup1 = $$et{SET_GROUP1};
9412 674 100 33     3035 if ($$tagInfo{SubDirectory} and $$tagInfo{SubDirectory}{TagTable} and
      66        
9413             $$tagInfo{SubDirectory}{TagTable} eq 'Image::ExifTool::QuickTime::Track')
9414             {
9415 38 100       107 $track or $track = 0;
9416 38         108 $$et{SET_GROUP1} = 'Track' . (++$track);
9417             }
9418 674         1721 my $subTable = GetTagTable($$subdir{TagTable});
9419 674         1050 my $proc = $$subdir{ProcessProc};
9420             # make ProcessMOV() the default processing procedure for subdirectories
9421 674 100 100     2320 $proc = \&ProcessMOV unless $proc or $$subTable{PROCESS_PROC};
9422 674 50       1216 if ($size > $start) {
9423             # delay processing of ipco box until after all other boxes
9424 674 100 66     1473 if ($tag eq 'ipco' and not $$et{IsItemProperty}) {
9425 3         919 $$et{ItemPropertyContainer} = [ \%dirInfo, $subTable, $proc ];
9426 3         15 $et->VPrint(0,"$$et{INDENT}\[Process ipco box later]");
9427             } else {
9428 671         1813 $et->ProcessDirectory(\%dirInfo, $subTable, $proc);
9429             }
9430             }
9431 674 100       1709 if ($tag eq 'stbl') {
    100          
9432             # process sample data when exiting SampleTable box if extracting embedded
9433 38 100       132 ProcessSamples($et) if $ee;
9434             } elsif ($tag eq 'minf') {
9435 38         87 $$et{HandlerType} = ''; # reset handler type at end of media info box
9436             }
9437 674         1130 $$et{SET_GROUP1} = $oldGroup1;
9438 674         1220 SetByteOrder('MM');
9439             } elsif ($hasData) {
9440             # handle atoms containing 'data' tags
9441             # (currently ignore contained atoms: 'itif', 'name', etc.)
9442 157         220 my $pos = 0;
9443 157         180 for (;;) {
9444 318 100       640 last if $pos + 16 > $size;
9445 161         632 my ($len, $type, $flags, $ctry, $lang) = unpack("x${pos}Na4Nnn", $val);
9446 161 50 33     535 last if $pos + $len > $size or not $len;
9447 161         246 my ($value, $langInfo, $oldDir);
9448 161         254 my $format = $$tagInfo{Format};
9449 161 50 33     456 if ($type eq 'data' and $len >= 16) {
9450 161         196 $pos += 16;
9451 161         189 $len -= 16;
9452 161         292 $value = substr($val, $pos, $len);
9453             # format flags (ref 12):
9454             # 0x0=binary, 0x1=UTF-8, 0x2=UTF-16, 0x3=ShiftJIS,
9455             # 0x4=UTF-8 0x5=UTF-16, 0xd=JPEG, 0xe=PNG,
9456             # 0x15=signed int, 0x16=unsigned int, 0x17=float,
9457             # 0x18=double, 0x1b=BMP, 0x1c='meta' atom
9458 161 100       318 if ($stringEncoding{$flags}) {
9459             # handle all string formats
9460 116         327 $value = $et->Decode($value, $stringEncoding{$flags});
9461             # (shouldn't be null terminated, but some software writes it anyway)
9462 116 50       320 $value =~ s/\0$// unless $$tagInfo{Binary};
9463             } else {
9464 45 100       166 if (not $format) {
    100          
9465 12         35 $format = QuickTimeFormat($flags, $len);
9466             } elsif ($format =~ /^int\d+([us])$/) {
9467             # adjust integer to available length (but not int64)
9468 15         71 my $fmt = { 1=>'int8', 2=>'int16', 4=>'int32' }->{$len};
9469 15 50       71 $format = $fmt . $1 if defined $fmt;
9470             }
9471 45 100       100 if ($format) {
    50          
9472 33         130 $value = ReadValue(\$value, 0, $format, $$tagInfo{Count}, $len);
9473             } elsif (not $$tagInfo{ValueConv}) {
9474             # make binary data a scalar reference unless a ValueConv exists
9475 12         21 my $buf = $value;
9476 12         26 $value = \$buf;
9477             }
9478             }
9479             }
9480 161 100 66     483 if ($ctry or $lang) {
9481 1         5 my $langCode = GetLangCode($lang, $ctry);
9482 1 50       5 if ($langCode) {
9483             # get tagInfo for other language
9484 1         4 $langInfo = GetLangInfoQT($et, $tagInfo, $langCode);
9485             # save other language tag ID's so we can delete later if necessary
9486 1 50       3 if ($langInfo) {
9487 1 50       6 $$tagInfo{OtherLang} or $$tagInfo{OtherLang} = [ ];
9488 1         2 push @{$$tagInfo{OtherLang}}, $$langInfo{TagID};
  1         3  
9489             }
9490             }
9491             }
9492 161 100       315 $langInfo or $langInfo = $tagInfo;
9493 161 0       248 $et->VerboseInfo($tag, $langInfo,
    50          
9494             Value => ref $value ? $$value : $value,
9495             DataPt => \$val,
9496             DataPos => $dataPos,
9497             Start => $pos,
9498             Size => $len,
9499             Format => $format,
9500             Index => $index,
9501             Extra => sprintf(", Type='${type}', Flags=0x%x, Lang=0x%.4x",$flags,$lang),
9502             ) if $verbose;
9503             # use "Keys" in path instead of ItemList if this was defined by a Keys tag
9504 161   100     769 my $isKey = $$tagInfo{Groups} && $$tagInfo{Groups}{1} && $$tagInfo{Groups}{1} eq 'Keys';
9505 161 100       286 if ($isKey) {
9506 22         43 $oldDir = $$et{PATH}[-1];
9507 22         43 $$et{PATH}[-1] = 'Keys';
9508             }
9509 161 50       500 $et->FoundTag($langInfo, $value) if defined $value;
9510 161 100       280 $$et{PATH}[-1] = $oldDir if $isKey;
9511 161         272 $pos += $len;
9512             }
9513             } elsif ($tag =~ /^\xa9/ or $$tagInfo{IText}) {
9514             # parse international text to extract all languages
9515 49         83 my $pos = 0;
9516 49 50       109 if ($$tagInfo{Format}) {
9517 0         0 $et->FoundTag($tagInfo, ReadValue(\$val, 0, $$tagInfo{Format}, undef, length($val)));
9518 0         0 $pos = $size;
9519             }
9520 49         61 for (;;) {
9521 98         132 my ($len, $lang);
9522 98 100 66     213 if ($$tagInfo{IText} and $$tagInfo{IText} >= 6) {
9523 4 100       13 last if $pos + $$tagInfo{IText} > $size;
9524 2         5 $pos += $$tagInfo{IText} - 2;
9525 2         7 $lang = unpack("x${pos}n", $val);
9526 2         3 $pos += 2;
9527 2         3 $len = $size - $pos;
9528             } else {
9529 94 100       202 last if $pos + 4 > $size;
9530 47         137 ($len, $lang) = unpack("x${pos}nn", $val);
9531 47         128 $pos += 4;
9532             # according to the QuickTime spec (ref 12), $len should include
9533             # 4 bytes for length and type words, but nobody (including
9534             # Apple, Pentax and Kodak) seems to add these in, so try
9535             # to allow for either
9536 47 50       111 if ($pos + $len > $size) {
9537 0         0 $len -= 4;
9538 0 0 0     0 last if $pos + $len > $size or $len < 0;
9539             }
9540             }
9541             # ignore any empty entries (or null padding) after the first
9542 49 50 33     102 next if not $len and $pos;
9543 49         105 my $str = substr($val, $pos, $len);
9544 49         61 my ($langInfo, $enc);
9545 49 50 33     212 if (($lang < 0x400 or $lang == 0x7fff) and $str !~ /^\xfe\xff/) {
      33        
9546             # this is a Macintosh language code
9547             # a language code of 0 is Macintosh english, so treat as default
9548 49 50       93 if ($lang) {
9549 0 0       0 if ($lang == 0x7fff) {
9550             # technically, ISO 639-2 doesn't have a 2-character
9551             # equivalent for 'und', but use 'un' anyway
9552 0         0 $lang = 'un';
9553             } else {
9554             # use Font.pm to look up language string
9555 0         0 require Image::ExifTool::Font;
9556 0         0 $lang = $Image::ExifTool::Font::ttLang{Macintosh}{$lang};
9557             }
9558             }
9559             # the spec says only "Macintosh text encoding", but
9560             # allow this to be configured by the user
9561 49         70 $enc = $charsetQuickTime;
9562             } else {
9563             # convert language code to ASCII (ignore read-only bit)
9564 0         0 $lang = UnpackLang($lang);
9565             # may be either UTF-8 or UTF-16BE
9566 0 0       0 $enc = $str=~s/^\xfe\xff// ? 'UTF16' : 'UTF8';
9567             }
9568 49 100       102 unless ($$tagInfo{NoDecode}) {
9569 48         131 $str = $et->Decode($str, $enc);
9570 48         95 $str =~ s/\0+$//; # remove any trailing nulls (eg. 3gp tags)
9571             }
9572 49 100 100     130 if ($$tagInfo{IText} and $$tagInfo{IText} > 6) {
9573 1         8 my $n = $$tagInfo{IText} - 6;
9574             # add back extra bytes (eg. 'rtng' box)
9575 1         6 $str = substr($val, $pos-$n-2, $n) . $str;
9576             }
9577 49 50       77 $langInfo = GetLangInfoQT($et, $tagInfo, $lang) if $lang;
9578 49   33     205 $et->FoundTag($langInfo || $tagInfo, $str);
9579 49         105 $pos += $len;
9580             }
9581             } else {
9582 251         393 my $format = $$tagInfo{Format};
9583 251 100       432 if ($format) {
9584 68         293 $val = ReadValue(\$val, 0, $format, $$tagInfo{Count}, length($val));
9585             }
9586 251         340 my $oldBase;
9587 251 50       512 if ($$tagInfo{SetBase}) {
9588 0         0 $oldBase = $$et{BASE};
9589 0         0 $$et{BASE} = $dataPos;
9590             }
9591 251         609 my $key = $et->FoundTag($tagInfo, $val);
9592 251 50       568 $$et{BASE} = $oldBase if defined $oldBase;
9593             # decode if necessary (NOTE: must be done after RawConv)
9594 251 50 66     1420 if (defined $key and (not $format or $format =~ /^string/) and
      100        
      100        
      66        
      100        
      66        
      66        
9595             not $$tagInfo{Unknown} and not $$tagInfo{ValueConv} and
9596             not $$tagInfo{Binary} and defined $$et{VALUE}{$key} and not ref $val)
9597             {
9598 20         50 my $vp = \$$et{VALUE}{$key};
9599 20 50 66     147 if (not ref $$vp and length($$vp) <= 65536 and $$vp =~ /[\x80-\xff]/) {
      66        
9600             # the encoding of this is not specified, so use CharsetQuickTime
9601             # unless the string is valid UTF-8
9602 0         0 require Image::ExifTool::XMP;
9603 0 0       0 my $enc = Image::ExifTool::XMP::IsUTF8($vp) > 0 ? 'UTF8' : $charsetQuickTime;
9604 0         0 $$vp = $et->Decode($$vp, $enc);
9605             }
9606             }
9607             }
9608             }
9609             } else {
9610 88 50       150 $et->VerboseInfo($tag, $tagInfo,
9611             Size => $size,
9612             Extra => sprintf(' at offset 0x%.4x', $raf->Tell()),
9613             ) if $verbose;
9614 88 50 33     302 if ($size and (not $raf->Seek($size-1, 1) or $raf->Read($buff, 1) != 1)) {
      66        
9615 0         0 my $t = PrintableTagID($tag,2);
9616 0         0 $et->Warn("Truncated '${t}' data");
9617 0         0 last;
9618             }
9619             }
9620 1253         1788 $dataPos += $size + 8; # point to start of next atom data
9621 1253 100 100     3545 last if $dirEnd and $dataPos >= $dirEnd; # (note: ignores last value if 0 bytes)
9622 890 100       2180 $raf->Read($buff, 8) == 8 or last;
9623 870         2667 ($size, $tag) = unpack('Na4', $buff);
9624 870 100       1925 ++$index if defined $index;
9625             }
9626             # tweak file type based on track content ("iso*" and "dash" ftyp only)
9627 383 0 66     872 if ($topLevel and $$et{VALUE}{FileType} and $$et{VALUE}{FileType} eq 'MP4' and
      33        
      33        
      0        
      0        
      0        
      0        
9628             $$et{save_ftyp} and $$et{HasHandler} and $$et{save_ftyp} =~ /^(iso|dash)/ and
9629             $$et{HasHandler}{soun} and not $$et{HasHandler}{vide})
9630             {
9631 0         0 $et->OverrideFileType('M4A', 'audio/mp4');
9632             }
9633             # fill in missing defaults for alternate language tags
9634             # (the first language is taken as the default)
9635 383 100 100     696 if ($doDefaultLang and $$et{QTLang}) {
9636 1         3 QTLang: foreach $tag (@{$$et{QTLang}}) {
  1         4  
9637 1 50       4 next unless defined $$et{VALUE}{$tag};
9638 1 50       5 my $langInfo = $$et{TAG_INFO}{$tag} or next;
9639 1 50       5 my $tagInfo = $$langInfo{SrcTagInfo} or next;
9640 1         3 my $infoHash = $$et{TAG_INFO};
9641 1         2 my $name = $$tagInfo{Name};
9642             # loop through all instances of this tag name and generate the default-language
9643             # version only if we don't already have a QuickTime tag with this name
9644 1         3 my ($i, $key);
9645 1         5 for ($i=0, $key=$name; $$infoHash{$key}; ++$i, $key="$name ($i)") {
9646 1 50       6 next QTLang if $et->GetGroup($key, 0) eq 'QuickTime';
9647             }
9648 0         0 $et->FoundTag($tagInfo, $$et{VALUE}{$tag});
9649             }
9650 1         4 delete $$et{QTLang};
9651             }
9652             # process item information now that we are done processing its 'meta' container
9653 383 100 100     1158 HandleItemInfo($et) if $topLevel or $dirID eq 'meta';
9654              
9655 383 100 100     840 ScanMediaData($et) if $ee and $topLevel; # brute force scan for metadata embedded in media data
9656              
9657             # restore any changed options
9658 383         786 $et->Options($_ => $saveOptions{$_}) foreach keys %saveOptions;
9659 383         1448 return 1;
9660             }
9661              
9662             #------------------------------------------------------------------------------
9663             # Process a QuickTime Image File
9664             # Inputs: 0) ExifTool object reference, 1) directory information reference
9665             # Returns: 1 on success
9666             sub ProcessQTIF($$)
9667             {
9668 0     0 0   my ($et, $dirInfo) = @_;
9669 0           my $table = GetTagTable('Image::ExifTool::QuickTime::ImageFile');
9670 0           return ProcessMOV($et, $dirInfo, $table);
9671             }
9672              
9673             1; # end
9674              
9675             __END__