File Coverage

blib/lib/Image/ExifTool/QuickTime.pm
Criterion Covered Total %
statement 702 1148 61.1
branch 371 842 44.0
condition 221 509 43.4
subroutine 32 44 72.7
pod 0 34 0.0
total 1326 2577 51.4


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: QuickTime.pm
3             #
4             # Description: Read QuickTime and MP4 meta information
5             #
6             # Revisions: 10/04/2005 - P. Harvey Created
7             # 12/19/2005 - P. Harvey Added MP4 support
8             # 09/22/2006 - P. Harvey Added M4A support
9             # 07/27/2010 - P. Harvey Updated to 2010-05-03 QuickTime spec
10             #
11             # References:
12             #
13             # 1) http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html
14             # 2) http://search.cpan.org/dist/MP4-Info-1.04/
15             # 3) http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
16             # 4) http://wiki.multimedia.cx/index.php?title=Apple_QuickTime
17             # 5) ISO 14496-12 (http://read.pudn.com/downloads64/ebook/226547/ISO_base_media_file_format.pdf)
18             # 6) ISO 14496-16 (http://www.iec-normen.de/previewpdf/info_isoiec14496-16%7Bed2.0%7Den.pdf)
19             # 7) http://atomicparsley.sourceforge.net/mpeg-4files.html
20             # 8) http://wiki.multimedia.cx/index.php?title=QuickTime_container
21             # 9) http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart3.pdf (Oct 2008)
22             # 10) http://code.google.com/p/mp4v2/wiki/iTunesMetadata
23             # 11) http://www.canieti.com.mx/assets/files/1011/IEC_100_1384_DC.pdf
24             # 12) QuickTime file format specification 2010-05-03
25             # 13) http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf
26             # 14) http://standards.iso.org/ittf/PubliclyAvailableStandards/c051533_ISO_IEC_14496-12_2008.zip
27             # 15) http://getid3.sourceforge.net/source/module.audio-video.quicktime.phps
28             # 16) http://qtra.apple.com/atoms.html
29             # 17) http://www.etsi.org/deliver/etsi_ts/126200_126299/126244/10.01.00_60/ts_126244v100100p.pdf
30             # 18) https://github.com/appsec-labs/iNalyzer/blob/master/scinfo.m
31             # 19) http://nah6.com/~itsme/cvs-xdadevtools/iphone/tools/decodesinf.pl
32             # 20) https://developer.apple.com/legacy/library/documentation/quicktime/reference/QT7-1_Update_Reference/QT7-1_Update_Reference.pdf
33             # 21) Francois Bonzon private communication
34             # 22) https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html
35             # 23) http://atomicparsley.sourceforge.net/mpeg-4files.html
36             # 24) https://github.com/sergiomb2/libmp4v2/wiki/iTunesMetadata
37             # 25) https://cconcolato.github.io/mp4ra/atoms.html
38             # 26) https://github.com/SamsungVR/android_upload_sdk/blob/master/SDKLib/src/main/java/com/samsung/msca/samsungvr/sdk/UserVideo.java
39             # 27) https://exiftool.org/forum/index.php?topic=11517.0
40             # 28) https://docs.mp3tag.de/mapping/
41             # 29) https://developer.apple.com/documentation/quicktime-file-format/media_data_reference_atom
42             #------------------------------------------------------------------------------
43              
44             package Image::ExifTool::QuickTime;
45              
46 31     31   5633 use strict;
  31         49  
  31         1200  
47 31     31   116 use vars qw($VERSION $AUTOLOAD %stringEncoding %avType %dontInherit %eeBox);
  31         51  
  31         1959  
48 31     31   127 use Image::ExifTool qw(:DataAccess :Utils);
  31         46  
  31         5880  
49 31     31   2564 use Image::ExifTool::Exif;
  31         54  
  31         1058  
50 31     31   1510 use Image::ExifTool::GPS;
  31         51  
  31         423403  
51              
52             $VERSION = '3.33';
53              
54             sub ProcessMOV($$;$);
55             sub ProcessKeys($$$);
56             sub ProcessMetaKeys($$$);
57             sub ProcessMetaData($$$);
58             sub ProcessEncodingParams($$$);
59             sub ProcessSampleDesc($$$);
60             sub ProcessHybrid($$$);
61             sub ProcessRights($$$);
62             sub ProcessNextbase($$$);
63             sub Process_mrlh($$$);
64             sub Process_mrlv($$$);
65             sub Process_mrld($$$);
66             # ++vvvvvvvvvvvv++ (in QuickTimeStream.pl)
67             sub Process_mebx($$$);
68             sub Process_3gf($$$);
69             sub Process_gps0($$$);
70             sub Process_gsen($$$);
71             sub Process_gdat($$$);
72             sub Process_nbmt($$$);
73             sub ProcessKenwood($$$);
74             sub ProcessRIFFTrailer($$$);
75             sub ProcessTTAD($$$);
76             sub ProcessNMEA($$$);
77             sub ProcessGPSLog($$$);
78             sub ProcessGarminGPS($$$);
79             sub SaveMetaKeys($$$);
80             # ++^^^^^^^^^^^^++
81             sub ParseItemLocation($$);
82             sub ParseContentDescribes($$);
83             sub ParseItemInfoEntry($$);
84             sub ParseItemPropAssoc($$);
85             sub FixWrongFormat($);
86             sub GetMatrixStructure($$);
87             sub ConvertISO6709($);
88             sub ConvInvISO6709($);
89             sub ConvertChapterList($);
90             sub PrintChapter($);
91             sub PrintGPSCoordinates($);
92             sub PrintInvGPSCoordinates($);
93             sub UnpackLang($;$);
94             sub WriteKeys($$$);
95             sub WriteQuickTime($$$);
96             sub WriteMOV($$);
97             sub WriteNextbase($$$);
98             sub GetLangInfo($$);
99             sub CheckQTValue($$$);
100              
101             # MIME types for all entries in the ftypLookup with file extensions
102             # (defaults to 'video/mp4' if not found in this lookup)
103             my %mimeLookup = (
104             '3G2' => 'video/3gpp2',
105             '3GP' => 'video/3gpp',
106             AAX => 'audio/vnd.audible.aax',
107             DVB => 'video/vnd.dvb.file',
108             F4A => 'audio/mp4',
109             F4B => 'audio/mp4',
110             JP2 => 'image/jp2',
111             JPM => 'image/jpm',
112             JPX => 'image/jpx',
113             M4A => 'audio/mp4',
114             M4B => 'audio/mp4',
115             M4P => 'audio/mp4',
116             M4V => 'video/x-m4v',
117             MOV => 'video/quicktime',
118             MQV => 'video/quicktime',
119             HEIC => 'image/heic',
120             HEVC => 'image/heic-sequence',
121             HEICS=> 'image/heic-sequence',
122             HEIF => 'image/heif',
123             HEIFS=> 'image/heif-sequence',
124             AVIF => 'image/avif', #PH (NC)
125             CRX => 'video/x-canon-crx', # (will get overridden)
126             );
127              
128             # look up file type from ftyp atom type, with MIME type in comment if known
129             # (ref http://www.ftyps.com/)
130             my %ftypLookup = (
131             '3g2a' => '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-0 V1.0', # video/3gpp2
132             '3g2b' => '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-A V1.0.0', # video/3gpp2
133             '3g2c' => '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-B v1.0', # video/3gpp2
134             '3ge6' => '3GPP (.3GP) Release 6 MBMS Extended Presentations', # video/3gpp
135             '3ge7' => '3GPP (.3GP) Release 7 MBMS Extended Presentations', # video/3gpp
136             '3gg6' => '3GPP Release 6 General Profile', # video/3gpp
137             '3gp1' => '3GPP Media (.3GP) Release 1 (probably non-existent)', # video/3gpp
138             '3gp2' => '3GPP Media (.3GP) Release 2 (probably non-existent)', # video/3gpp
139             '3gp3' => '3GPP Media (.3GP) Release 3 (probably non-existent)', # video/3gpp
140             '3gp4' => '3GPP Media (.3GP) Release 4', # video/3gpp
141             '3gp5' => '3GPP Media (.3GP) Release 5', # video/3gpp
142             '3gp6' => '3GPP Media (.3GP) Release 6 Basic Profile', # video/3gpp
143             '3gp6' => '3GPP Media (.3GP) Release 6 Progressive Download', # video/3gpp
144             '3gp6' => '3GPP Media (.3GP) Release 6 Streaming Servers', # video/3gpp
145             '3gs7' => '3GPP Media (.3GP) Release 7 Streaming Servers', # video/3gpp
146             'aax ' => 'Audible Enhanced Audiobook (.AAX)', #PH
147             'avc1' => 'MP4 Base w/ AVC ext [ISO 14496-12:2005]', # video/mp4
148             'CAEP' => 'Canon Digital Camera',
149             'caqv' => 'Casio Digital Camera',
150             'CDes' => 'Convergent Design',
151             'da0a' => 'DMB MAF w/ MPEG Layer II aud, MOT slides, DLS, JPG/PNG/MNG images',
152             'da0b' => 'DMB MAF, extending DA0A, with 3GPP timed text, DID, TVA, REL, IPMP',
153             'da1a' => 'DMB MAF audio with ER-BSAC audio, JPG/PNG/MNG images',
154             'da1b' => 'DMB MAF, extending da1a, with 3GPP timed text, DID, TVA, REL, IPMP',
155             'da2a' => 'DMB MAF aud w/ HE-AAC v2 aud, MOT slides, DLS, JPG/PNG/MNG images',
156             'da2b' => 'DMB MAF, extending da2a, with 3GPP timed text, DID, TVA, REL, IPMP',
157             'da3a' => 'DMB MAF aud with HE-AAC aud, JPG/PNG/MNG images',
158             'da3b' => 'DMB MAF, extending da3a w/ BIFS, 3GPP timed text, DID, TVA, REL, IPMP',
159             'dmb1' => 'DMB MAF supporting all the components defined in the specification',
160             'dmpf' => 'Digital Media Project', # various
161             'drc1' => 'Dirac (wavelet compression), encapsulated in ISO base media (MP4)',
162             'dv1a' => 'DMB MAF vid w/ AVC vid, ER-BSAC aud, BIFS, JPG/PNG/MNG images, TS',
163             'dv1b' => 'DMB MAF, extending dv1a, with 3GPP timed text, DID, TVA, REL, IPMP',
164             'dv2a' => 'DMB MAF vid w/ AVC vid, HE-AAC v2 aud, BIFS, JPG/PNG/MNG images, TS',
165             'dv2b' => 'DMB MAF, extending dv2a, with 3GPP timed text, DID, TVA, REL, IPMP',
166             'dv3a' => 'DMB MAF vid w/ AVC vid, HE-AAC aud, BIFS, JPG/PNG/MNG images, TS',
167             'dv3b' => 'DMB MAF, extending dv3a, with 3GPP timed text, DID, TVA, REL, IPMP',
168             'dvr1' => 'DVB (.DVB) over RTP', # video/vnd.dvb.file
169             'dvt1' => 'DVB (.DVB) over MPEG-2 Transport Stream', # video/vnd.dvb.file
170             'F4A ' => 'Audio for Adobe Flash Player 9+ (.F4A)', # audio/mp4
171             'F4B ' => 'Audio Book for Adobe Flash Player 9+ (.F4B)', # audio/mp4
172             'F4P ' => 'Protected Video for Adobe Flash Player 9+ (.F4P)', # video/mp4
173             'F4V ' => 'Video for Adobe Flash Player 9+ (.F4V)', # video/mp4
174             'isc2' => 'ISMACryp 2.0 Encrypted File', # ?/enc-isoff-generic
175             'iso2' => 'MP4 Base Media v2 [ISO 14496-12:2005]', # video/mp4 (or audio)
176             'iso3' => 'MP4 Base Media v3', # video/mp4 (or audio)
177             'iso4' => 'MP4 Base Media v4', # video/mp4 (or audio)
178             'iso5' => 'MP4 Base Media v5', # video/mp4 (or audio)
179             'iso6' => 'MP4 Base Media v6', # video/mp4 (or audio)
180             'iso7' => 'MP4 Base Media v7', # video/mp4 (or audio)
181             'iso8' => 'MP4 Base Media v8', # video/mp4 (or audio)
182             'iso9' => 'MP4 Base Media v9', # video/mp4 (or audio)
183             'isom' => 'MP4 Base Media v1 [IS0 14496-12:2003]', # video/mp4 (or audio)
184             'JP2 ' => 'JPEG 2000 Image (.JP2) [ISO 15444-1 ?]', # image/jp2
185             'JP20' => 'Unknown, from GPAC samples (prob non-existent)',
186             'jpm ' => 'JPEG 2000 Compound Image (.JPM) [ISO 15444-6]', # image/jpm
187             'jpx ' => 'JPEG 2000 with extensions (.JPX) [ISO 15444-2]', # image/jpx
188             'KDDI' => '3GPP2 EZmovie for KDDI 3G cellphones', # video/3gpp2
189             #LCAG => (found in CompatibleBrands of Leica MOV videos)
190             'M4A ' => 'Apple iTunes AAC-LC (.M4A) Audio', # audio/x-m4a
191             'M4B ' => 'Apple iTunes AAC-LC (.M4B) Audio Book', # audio/mp4
192             'M4P ' => 'Apple iTunes AAC-LC (.M4P) AES Protected Audio', # audio/mp4
193             'M4V ' => 'Apple iTunes Video (.M4V) Video', # video/x-m4v
194             'M4VH' => 'Apple TV (.M4V)', # video/x-m4v
195             'M4VP' => 'Apple iPhone (.M4V)', # video/x-m4v
196             'mj2s' => 'Motion JPEG 2000 [ISO 15444-3] Simple Profile', # video/mj2
197             'mjp2' => 'Motion JPEG 2000 [ISO 15444-3] General Profile', # video/mj2
198             'mmp4' => 'MPEG-4/3GPP Mobile Profile (.MP4/3GP) (for NTT)', # video/mp4
199             'mp21' => 'MPEG-21 [ISO/IEC 21000-9]', # various
200             'mp41' => 'MP4 v1 [ISO 14496-1:ch13]', # video/mp4
201             'mp42' => 'MP4 v2 [ISO 14496-14]', # video/mp4
202             'mp71' => 'MP4 w/ MPEG-7 Metadata [per ISO 14496-12]', # various
203             'MPPI' => 'Photo Player, MAF [ISO/IEC 23000-3]', # various
204             'mqt ' => 'Sony / Mobile QuickTime (.MQV) US Patent 7,477,830 (Sony Corp)', # video/quicktime
205             'MSNV' => 'MPEG-4 (.MP4) for SonyPSP', # audio/mp4
206             'NDAS' => 'MP4 v2 [ISO 14496-14] Nero Digital AAC Audio', # audio/mp4
207             'NDSC' => 'MPEG-4 (.MP4) Nero Cinema Profile', # video/mp4
208             'NDSH' => 'MPEG-4 (.MP4) Nero HDTV Profile', # video/mp4
209             'NDSM' => 'MPEG-4 (.MP4) Nero Mobile Profile', # video/mp4
210             'NDSP' => 'MPEG-4 (.MP4) Nero Portable Profile', # video/mp4
211             'NDSS' => 'MPEG-4 (.MP4) Nero Standard Profile', # video/mp4
212             'NDXC' => 'H.264/MPEG-4 AVC (.MP4) Nero Cinema Profile', # video/mp4
213             'NDXH' => 'H.264/MPEG-4 AVC (.MP4) Nero HDTV Profile', # video/mp4
214             'NDXM' => 'H.264/MPEG-4 AVC (.MP4) Nero Mobile Profile', # video/mp4
215             'NDXP' => 'H.264/MPEG-4 AVC (.MP4) Nero Portable Profile', # video/mp4
216             'NDXS' => 'H.264/MPEG-4 AVC (.MP4) Nero Standard Profile', # video/mp4
217             'odcf' => 'OMA DCF DRM Format 2.0 (OMA-TS-DRM-DCF-V2_0-20060303-A)', # various
218             'opf2' => 'OMA PDCF DRM Format 2.1 (OMA-TS-DRM-DCF-V2_1-20070724-C)',
219             'opx2' => 'OMA PDCF DRM + XBS extensions (OMA-TS-DRM_XBS-V1_0-20070529-C)',
220             'pana' => 'Panasonic Digital Camera',
221             'qt ' => 'Apple QuickTime (.MOV/QT)', # video/quicktime
222             'ROSS' => 'Ross Video',
223             'sdv ' => 'SD Memory Card Video', # various?
224             'ssc1' => 'Samsung stereoscopic, single stream',
225             'ssc2' => 'Samsung stereoscopic, dual stream',
226             'XAVC' => 'Sony XAVC', #PH
227             'heic' => 'High Efficiency Image Format HEVC still image (.HEIC)', # image/heic
228             'hevc' => 'High Efficiency Image Format HEVC sequence (.HEICS)', # image/heic-sequence
229             'mif1' => 'High Efficiency Image Format still image (.HEIF)', # image/heif
230             'msf1' => 'High Efficiency Image Format sequence (.HEIFS)', # image/heif-sequence
231             'heix' => 'High Efficiency Image Format still image (.HEIF)', # image/heif (ref PH, Canon 1DXmkIII)
232             'avif' => 'AV1 Image File Format (.AVIF)', # image/avif
233             'crx ' => 'Canon Raw (.CRX)', #PH (CR3 or CRM; use Canon CompressorVersion to decide)
234             );
235              
236             # use extension to determine file type
237             my %useExt = ( GLV => 'MP4' );
238              
239             # information for int32u date/time tags (time zero is Jan 1, 1904)
240             my %timeInfo = (
241             Notes => q{
242             converted from UTC to local time if the QuickTimeUTC option is set. This
243             tag is part of a binary data structure so it may not be deleted -- instead
244             the value is set to zero if the tag is deleted individually
245             },
246             Shift => 'Time',
247             Writable => 1,
248             Permanent => 1,
249             DelValue => 0,
250             # It is not uncommon for brain-dead software to use the wrong time zero, it should be
251             # Jan 1, 1904, so assume a time zero of Jan 1, 1970 if the date is before this
252             # Note: This value will be in UTC if generated by a system that is aware of the time zone
253             # (also note: this code is duplicated for the CreateDate tag)
254             RawConv => q{
255             if ($val) {
256             my $offset = (66 * 365 + 17) * 24 * 3600;
257             if ($val >= $offset or $$self{OPTIONS}{QuickTimeUTC}) {
258             $val -= $offset;
259             } elsif (not $$self{IsWriting}) {
260             $self->Warn('Patched incorrect time zero for QuickTime date/time tag',1);
261             }
262             } else {
263             undef $val if $self->Options('StrictDate');
264             }
265             return $val;
266             },
267             RawConvInv => q{
268             if ($val and $$self{FileType} eq 'CR3' and not $self->Options('QuickTimeUTC')) {
269             # convert to UTC
270             my $offset = (66 * 365 + 17) * 24 * 3600;
271             $val = ConvertUnixTime($val - $offset);
272             $val = GetUnixTime($val, 1) + $offset;
273             }
274             return $val;
275             },
276             # (all CR3 files store UTC times - PH)
277             ValueConv => 'ConvertUnixTime($val, $self->Options("QuickTimeUTC") || $$self{FileType} eq "CR3")',
278             ValueConvInv => q{
279             $val = GetUnixTime($val, $self->Options("QuickTimeUTC"));
280             return undef unless defined $val;
281             return $val unless $val;
282             return $val + (66 * 365 + 17) * 24 * 3600;
283             },
284             PrintConv => '$self->ConvertDateTime($val)',
285             PrintConvInv => q{
286             return $val if $val eq '0000:00:00 00:00:00';
287             return $self->InverseDateTime($val);
288             }
289             # (can't put Groups here because they aren't constant!)
290             );
291             # properties for ISO 8601 format date/time tags
292             my %iso8601Date = (
293             Shift => 'Time',
294             ValueConv => q{
295             require Image::ExifTool::XMP;
296             $val = Image::ExifTool::XMP::ConvertXMPDate($val);
297             $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
298             return $val;
299             },
300             ValueConvInv => q{
301             require Image::ExifTool::XMP;
302             my $tmp = Image::ExifTool::XMP::FormatXMPDate($val);
303             ($val = $tmp) =~ s/([-+]\d{2}):(\d{2})$/$1$2/ if defined $tmp; # remove time zone colon
304             return $val;
305             },
306             PrintConv => '$self->ConvertDateTime($val)',
307             PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
308             );
309             # information for duration tags
310             my %durationInfo = (
311             ValueConv => '$$self{TimeScale} ? $val / $$self{TimeScale} : $val',
312             PrintConv => '$$self{TimeScale} ? ConvertDuration($val) : $val',
313             );
314             # handle unknown tags
315             my %unknownInfo = (
316             Unknown => 1,
317             ValueConv => '$val =~ /^([\x20-\x7e]*)\0*$/ ? $1 : \$val',
318             );
319              
320             # multi-language text with 6-byte header
321             my %langText = ( IText => 6 );
322              
323             # parsing for most of the 3gp udta language text boxes
324             my %langText3gp = (
325             Notes => 'used in 3gp videos',
326             Avoid => 1,
327             IText => 6,
328             );
329              
330             # 4-character Vendor ID codes (ref PH)
331             my %vendorID = (
332             appl => 'Apple',
333             fe20 => 'Olympus (fe20)', # (FE200)
334             FFMP => 'FFmpeg',
335             'GIC '=> 'General Imaging Co.',
336             kdak => 'Kodak',
337             KMPI => 'Konica-Minolta',
338             leic => 'Leica',
339             mino => 'Minolta',
340             niko => 'Nikon',
341             NIKO => 'Nikon',
342             olym => 'Olympus',
343             pana => 'Panasonic',
344             pent => 'Pentax',
345             pr01 => 'Olympus (pr01)', # (FE100,FE110,FE115)
346             sany => 'Sanyo',
347             'SMI '=> 'Sorenson Media Inc.',
348             ZORA => 'Zoran Corporation',
349             'AR.D'=> 'Parrot AR.Drone',
350             ' KD '=> 'Kodak', # (FZ201)
351             );
352              
353             # QuickTime data atom encodings for string types (ref 12)
354             %stringEncoding = (
355             1 => 'UTF8',
356             2 => 'UTF16',
357             3 => 'ShiftJIS',
358             4 => 'UTF8',
359             5 => 'UTF16',
360             );
361              
362             # media types for which we have separate Keys tables (AudioKeys, VideoKeys)
363             %avType = (
364             soun => 'Audio',
365             vide => 'Video',
366             );
367              
368             # path to Keys/ItemList/UserData tags stored in tracks
369             my %trackPath = (
370             'MOV-Movie-Track-Meta-ItemList' => 'Keys',
371             'MOV-Movie-Track-UserData-Meta-ItemList' => 'ItemList',
372             'MOV-Movie-Track-UserData' => 'UserData',
373             );
374              
375             my %graphicsMode = (
376             # (ref http://homepage.mac.com/vanhoek/MovieGuts%20docs/64.html)
377             0x00 => 'srcCopy',
378             0x01 => 'srcOr',
379             0x02 => 'srcXor',
380             0x03 => 'srcBic',
381             0x04 => 'notSrcCopy',
382             0x05 => 'notSrcOr',
383             0x06 => 'notSrcXor',
384             0x07 => 'notSrcBic',
385             0x08 => 'patCopy',
386             0x09 => 'patOr',
387             0x0a => 'patXor',
388             0x0b => 'patBic',
389             0x0c => 'notPatCopy',
390             0x0d => 'notPatOr',
391             0x0e => 'notPatXor',
392             0x0f => 'notPatBic',
393             0x20 => 'blend',
394             0x21 => 'addPin',
395             0x22 => 'addOver',
396             0x23 => 'subPin',
397             0x24 => 'transparent',
398             0x25 => 'addMax',
399             0x26 => 'subOver',
400             0x27 => 'addMin',
401             0x31 => 'grayishTextOr',
402             0x32 => 'hilite',
403             0x40 => 'ditherCopy',
404             # the following ref ISO/IEC 15444-3
405             0x100 => 'Alpha',
406             0x101 => 'White Alpha',
407             0x102 => 'Pre-multiplied Black Alpha',
408             0x110 => 'Component Alpha',
409             );
410              
411             my %channelLabel = (
412             0xFFFFFFFF => 'Unknown',
413             0 => 'Unused',
414             100 => 'UseCoordinates',
415             1 => 'Left',
416             2 => 'Right',
417             3 => 'Center',
418             4 => 'LFEScreen',
419             5 => 'LeftSurround',
420             6 => 'RightSurround',
421             7 => 'LeftCenter',
422             8 => 'RightCenter',
423             9 => 'CenterSurround',
424             10 => 'LeftSurroundDirect',
425             11 => 'RightSurroundDirect',
426             12 => 'TopCenterSurround',
427             13 => 'VerticalHeightLeft',
428             14 => 'VerticalHeightCenter',
429             15 => 'VerticalHeightRight',
430             16 => 'TopBackLeft',
431             17 => 'TopBackCenter',
432             18 => 'TopBackRight',
433             33 => 'RearSurroundLeft',
434             34 => 'RearSurroundRight',
435             35 => 'LeftWide',
436             36 => 'RightWide',
437             37 => 'LFE2',
438             38 => 'LeftTotal',
439             39 => 'RightTotal',
440             40 => 'HearingImpaired',
441             41 => 'Narration',
442             42 => 'Mono',
443             43 => 'DialogCentricMix',
444             44 => 'CenterSurroundDirect',
445             45 => 'Haptic',
446             200 => 'Ambisonic_W',
447             201 => 'Ambisonic_X',
448             202 => 'Ambisonic_Y',
449             203 => 'Ambisonic_Z',
450             204 => 'MS_Mid',
451             205 => 'MS_Side',
452             206 => 'XY_X',
453             207 => 'XY_Y',
454             301 => 'HeadphonesLeft',
455             302 => 'HeadphonesRight',
456             304 => 'ClickTrack',
457             305 => 'ForeignLanguage',
458             400 => 'Discrete',
459             0x10000 => 'Discrete_0',
460             0x10001 => 'Discrete_1',
461             0x10002 => 'Discrete_2',
462             0x10003 => 'Discrete_3',
463             0x10004 => 'Discrete_4',
464             0x10005 => 'Discrete_5',
465             0x10006 => 'Discrete_6',
466             0x10007 => 'Discrete_7',
467             0x10008 => 'Discrete_8',
468             0x10009 => 'Discrete_9',
469             0x1000a => 'Discrete_10',
470             0x1000b => 'Discrete_11',
471             0x1000c => 'Discrete_12',
472             0x1000d => 'Discrete_13',
473             0x1000e => 'Discrete_14',
474             0x1000f => 'Discrete_15',
475             0x1ffff => 'Discrete_65535',
476             );
477              
478             my %qtFlags = ( #12
479             0 => 'undef', 22 => 'unsigned int', 71 => 'float[2] size',
480             1 => 'UTF-8', 23 => 'float', 72 => 'float[4] rect',
481             2 => 'UTF-16', 24 => 'double', 74 => 'int64s',
482             3 => 'ShiftJIS', 27 => 'BMP', 75 => 'int8u',
483             4 => 'UTF-8 sort', 28 => 'QT atom', 76 => 'int16u',
484             5 => 'UTF-16 sort', 65 => 'int8s', 77 => 'int32u',
485             13 => 'JPEG', 66 => 'int16s', 78 => 'int64u',
486             14 => 'PNG', 67 => 'int32s', 79 => 'double[3][3]',
487             21 => 'signed int', 70 => 'float[2] point',
488             );
489              
490             # properties which don't get inherited from the parent
491             # 1 = parent doesn't inherit this property
492             # 2 = parent doesn't inherit, but child does
493             %dontInherit = (
494             ispe => 1, # primary item must have an ispe and pixi, so no need to inherit these
495             pixi => 1,
496             irot => 1, # (tmap may have a different irot)
497             imir => 1, # (ditto)
498             pasp => 1, # (NC)
499             hvcC => 2, # (hvcC is a property of hvc1 referred to by primary grid)
500             colr => 2, # (colr is a property of primary grid or hvc1 referred to by primary)
501             );
502              
503             # tags that may be duplicated and directories that may contain duplicate tags
504             # (used only to avoid warnings when Validate-ing)
505             my %dupTagOK = ( mdat => 1, trak => 1, free => 1, infe => 1, sgpd => 1, dimg => 1, CCDT => 1,
506             sbgp => 1, csgm => 1, uuid => 1, cdsc => 1, maxr => 1, moof => 1, '----' => 1 );
507             my %dupDirOK = ( ipco => 1, iref => 1, sdpd => 1, moof => 1, traf => 1, '----' => 1 );
508              
509             # the usual atoms required to decode timed metadata with the ExtractEmbedded option
510             my %eeStd = ( stco => 'stbl', co64 => 'stbl', stsz => 'stbl', stz2 => 'stbl',
511             stsc => 'stbl', stts => 'stbl' );
512              
513             # atoms required for generating ImageDataHash
514             my %hashBox = ( vide => { %eeStd }, soun => { %eeStd } );
515              
516             # boxes and their containers for the various handler types that we want to save
517             # when the ExtractEmbedded is enabled (currently only the 'gps ' container name is
518             # used, but others have been checked against all available sample files and may be
519             # useful in the future if the names are used for different boxes on other locations)
520             %eeBox = (
521             # (note: vide is only processed if specific atoms exist in the VisualSampleDesc)
522             vide => { %eeStd, JPEG => 'stsd' },
523             text => { %eeStd },
524             meta => { %eeStd },
525             sbtl => { %eeStd },
526             data => { %eeStd },
527             camm => { %eeStd }, # (Insta360)
528             ctbx => { %eeStd }, # (GM cars)
529             '' => { 'gps ' => 'moov', 'GPS ' => 'main' }, # (no handler -- in top level 'moov' box, and main)
530             );
531             # boxes to save when ExtractEmbedded is set to 2 or higher
532             my %eeBox2 = (
533             vide => { avcC => 'stsd' }, # (parses H264 video stream)
534             );
535              
536             # image types in AVIF and HEIC files
537             my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
538              
539             my %userDefined = (
540             ALBUMARTISTSORT => 'AlbumArtistSort',
541             ASIN => 'ASIN',
542             );
543              
544             # QuickTime atoms
545             %Image::ExifTool::QuickTime::Main = (
546             PROCESS_PROC => \&ProcessMOV,
547             WRITE_PROC => \&WriteQuickTime, # (only needs to be defined for directories to process when writing)
548             GROUPS => { 2 => 'Video' },
549             meta => { # 'meta' is found here in my Sony ILCE-7S MP4 sample - PH
550             Name => 'Meta',
551             SubDirectory => {
552             TagTable => 'Image::ExifTool::QuickTime::Meta',
553             Start => 4, # skip 4-byte version number header
554             },
555             },
556             meco => { #ISO14496-12:2015
557             Name => 'OtherMeta',
558             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::OtherMeta' },
559             },
560             free => [
561             {
562             Name => 'KodakFree',
563             # (found in Kodak M5370 MP4 videos)
564             Condition => '$$valPt =~ /^\0\0\0.Seri/s',
565             SubDirectory => { TagTable => 'Image::ExifTool::Kodak::Free' },
566             },{
567             Name => 'Pittasoft',
568             # (Pittasoft Blackview dashcam MP4 videos)
569             Condition => '$$valPt =~ /^\0\0..(cprt|sttm|ptnm|ptrh|thum|gps |3gf )/s',
570             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Pittasoft' },
571             },{
572             Name => 'ThumbnailImage',
573             # (DJI Zenmuse XT2 thermal camera)
574             Groups => { 2 => 'Preview' },
575             Condition => '$$valPt =~ /^.{4}mdat\xff\xd8\xff/s',
576             RawConv => q{
577             my $len = unpack('N', $val);
578             return undef if $len <= 8 or $len > length($val);
579             return substr($val, 8, $len-8);
580             },
581             Binary => 1,
582             },{
583             Name => 'HighlightMarkers',
584             # (DJI Action 4, forum17700)
585             Notes => 'written by some DJI models',
586             Condition => '$$valPt =~ /^data.{4}hglg.{5}/s',
587             RawConv => q{
588             my $len = unpack 'x4N', $val;
589             return undef if $len < 13 or $len + 4 > length($val);
590             my $n = int(($len - 13) / 5);
591             my @a = map $_/1000, unpack "x17(xV)$n", $val;
592             return \@a;
593             },
594             },{
595             Unknown => 1,
596             Binary => 1,
597             },
598             # DJI videos also have block of offset/size of various atoms, eg)
599             # Atom name ???? Offset Size
600             # 0000: 63 6f 76 72 00 00 00 00 00 ed 6f da 00 0a 46 e0 [covr......o...F.]
601             # 0010: 73 6e 61 6c 00 00 00 00 00 f7 b6 d2 00 0a 46 e0 [snal..........F.]
602             # 0020: 68 67 6c 67 00 00 00 00 01 02 0a a2 00 00 00 21 [hglg...........!]
603             # 0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
604             # (also Samsung WB750 uncompressed thumbnail data starting with "SDIC\0")
605             ],
606             # fre1 - 4 bytes: "june" (Kodak PixPro SP360)
607             frea => {
608             Name => 'Kodak_frea',
609             SubDirectory => { TagTable => 'Image::ExifTool::Kodak::frea' },
610             },
611             skip => [
612             {
613             Name => 'CanonSkip',
614             Condition => '$$valPt =~ /^\0.{3}(CNDB|CNCV|CNMN|CNFV|CNTH|CNDM)/s',
615             SubDirectory => { TagTable => 'Image::ExifTool::Canon::Skip' },
616             },
617             {
618             Name => 'PreviewImage', # (found in DuDuBell M1 dashcam MOV files)
619             Groups => { 2 => 'Preview' },
620             Condition => '$$valPt =~ /^.{12}\xff\xd8\xff/',
621             Binary => 1,
622             RawConv => q{
623             my $len = Get32u(\$val, 8);
624             return undef unless length($val) >= $len + 12;
625             return substr($val, 12, $len);
626             },
627             },
628             {
629             Name => 'SkipInfo', # (found in 70mai Pro Plus+ MP4 videos)
630             # (look for something that looks like a QuickTime atom header)
631             Condition => '$$valPt =~ /^\0[\0-\x04]..[a-zA-Z ]{4}/s',
632             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SkipInfo' },
633             },
634             {
635             Name => 'LigoGPSInfo',
636             Condition => '$$valPt =~ /^LIGOGPSINFO\0/ and $$self{OPTIONS}{ExtractEmbedded}',
637             SubDirectory => {
638             TagTable => 'Image::ExifTool::QuickTime::Stream',
639             ProcessProc => 'Image::ExifTool::LigoGPS::ProcessLigoGPS',
640             },
641             },
642             {
643             Name => 'Skip',
644             RawConv => q{
645             if ($val =~ /^LIGOGPSINFO\0/) {
646             $self->Warn('Use the ExtractEmbedded option to decode timed GPS',3);
647             return undef;
648             }
649             return $val;
650             },
651             Unknown => 1,
652             Binary => 1,
653             },
654             ],
655             wide => { Unknown => 1, Binary => 1 },
656             ftyp => { #MP4
657             Name => 'FileType',
658             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::FileType' },
659             },
660             pnot => {
661             Name => 'Preview',
662             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Preview' },
663             },
664             PICT => {
665             Name => 'PreviewPICT',
666             Groups => { 2 => 'Preview' },
667             Binary => 1,
668             },
669             pict => { #8
670             Name => 'PreviewPICT',
671             Groups => { 2 => 'Preview' },
672             Binary => 1,
673             },
674             # (note that moov is present for an HEIF sequence)
675             moov => {
676             Name => 'Movie',
677             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Movie' },
678             },
679             moof => {
680             Name => 'MovieFragment',
681             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MovieFragment' },
682             },
683             # mfra - movie fragment random access: contains tfra (track fragment random access), and
684             # mfro (movie fragment random access offset) (ref 5)
685             mdat => { Name => 'MediaData', Unknown => 1, Binary => 1 },
686             'mdat-size' => {
687             Name => 'MediaDataSize',
688             RawConv => '$$self{MediaDataSize} = $val',
689             Notes => q{
690             not a real tag ID, this tag represents the size of the 'mdat' data in bytes
691             and is used in the AvgBitrate calculation
692             },
693             },
694             'mdat-offset' => {
695             Name => 'MediaDataOffset',
696             RawConv => '$$self{MediaDataOffset} = $val',
697             },
698             junk => { Unknown => 1, Binary => 1 }, #8
699             uuid => [
700             { #9 (MP4 files)
701             Name => 'XMP',
702             # *** this is where ExifTool writes XMP in MP4 videos (as per XMP spec) ***
703             Condition => '$$valPt=~/^\xbe\x7a\xcf\xcb\x97\xa9\x42\xe8\x9c\x71\x99\x94\x91\xe3\xaf\xac/',
704             WriteGroup => 'XMP', # (write main XMP tags here)
705             PreservePadding => 1,
706             SubDirectory => {
707             TagTable => 'Image::ExifTool::XMP::Main',
708             Start => 16,
709             },
710             },
711             { #11 (MP4 files)
712             Name => 'UUID-PROF',
713             Condition => '$$valPt=~/^PROF!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
714             SubDirectory => {
715             TagTable => 'Image::ExifTool::QuickTime::Profile',
716             Start => 24, # uid(16) + version(1) + flags(3) + count(4)
717             },
718             },
719             { #PH (Flip MP4 files)
720             Name => 'UUID-Flip',
721             Condition => '$$valPt=~/^\x4a\xb0\x3b\x0f\x61\x8d\x40\x75\x82\xb2\xd9\xfa\xce\xd3\x5f\xf5/',
722             SubDirectory => {
723             TagTable => 'Image::ExifTool::QuickTime::Flip',
724             Start => 16,
725             },
726             },
727             # "\x98\x7f\xa3\xdf\x2a\x85\x43\xc0\x8f\x8f\xd9\x7c\x47\x1e\x8e\xea" - unknown data in Flip videos
728             { #PH (Canon CR3)
729             Name => 'UUID-Canon2',
730             WriteLast => 1, # MUST come after mdat or DPP will drop mdat when writing!
731             Condition => '$$valPt=~/^\x21\x0f\x16\x87\x91\x49\x11\xe4\x81\x11\x00\x24\x21\x31\xfc\xe4/',
732             SubDirectory => {
733             TagTable => 'Image::ExifTool::Canon::uuid2',
734             Start => 16,
735             },
736             },
737             { # (ref https://github.com/JamesHeinrich/getID3/blob/master/getid3/module.audio-video.quicktime.php)
738             Name => 'SensorData', # sensor data for the 360Fly
739             Condition => '$$valPt=~/^\xef\xe1\x58\x9a\xbb\x77\x49\xef\x80\x95\x27\x75\x9e\xb1\xdc\x6f/ and $$self{OPTIONS}{ExtractEmbedded}',
740             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Tags360Fly' },
741             },{
742             Name => 'SensorData',
743             Condition => '$$valPt=~/^\xef\xe1\x58\x9a\xbb\x77\x49\xef\x80\x95\x27\x75\x9e\xb1\xdc\x6f/',
744             Notes => 'raw 360Fly sensor data without ExtractEmbedded option',
745             RawConv => q{
746             $self->Warn('Use the ExtractEmbedded option to decode timed SensorData',3);
747             return \$val;
748             },
749             },
750             { #https://c2pa.org/specifications/
751             Name => 'JUMBF',
752             Condition => '$$valPt=~/^\xd8\xfe\xc3\xd6\x1b\x0e\x48\x3c\x92\x97\x58\x28\x87\x7e\xc4\x81.{4}manifest\0/s',
753             Deletable => 1,
754             SubDirectory => {
755             TagTable => 'Image::ExifTool::Jpeg2000::Main',
756             DirName => 'JUMBF',
757             # 16 bytes uuid
758             # +4 bytes 0
759             # +9 bytes "manifest\0"
760             # +8 bytes absolute(!!!) offset to C2PA uuid "merkle\0" box
761             # =37 bytes total
762             Start => 37,
763             },
764             },
765             { #https://c2pa.org/specifications/ (NC)
766             Name => 'CBOR',
767             Condition => '$$valPt=~/^\xd8\xfe\xc3\xd6\x1b\x0e\x48\x3c\x92\x97\x58\x28\x87\x7e\xc4\x81.{4}merkle\0/s',
768             Deletable => 1, # (NC)
769             SubDirectory => {
770             TagTable => 'Image::ExifTool::CBOR::Main',
771             # 16 bytes uuid
772             # +4 bytes 0
773             # +7 bytes "merkle\0"
774             # =27 bytes total
775             Start => 27,
776             },
777             },
778             { #PH (Canon CR3)
779             Name => 'PreviewImage',
780             Condition => '$$valPt=~/^\xea\xf4\x2b\x5e\x1c\x98\x4b\x88\xb9\xfb\xb7\xdc\x40\x6e\x4d\x16.{32}/s',
781             Groups => { 2 => 'Preview' },
782             PreservePadding => 1,
783             # 0x00 - undef[16]: UUID
784             # 0x10 - int32u[2]: "0 1" (version and/or item count?)
785             # 0x18 - int32u: PRVW atom size
786             # 0x20 - int32u: 'PRVW'
787             # 0x30 - int32u: 0
788             # 0x34 - int16u: 1
789             # 0x36 - int16u: image width
790             # 0x38 - int16u: image height
791             # 0x3a - int16u: 1
792             # 0x3c - int32u: preview length
793             RawConv => '$val = substr($val, 0x30); $self->ValidateImage(\$val, $tag)',
794             },
795             { #PH (Garmin MP4)
796             Name => 'ThumbnailImage',
797             Condition => '$$valPt=~/^\x11\x6e\x40\xdc\xb1\x86\x46\xe4\x84\x7c\xd9\xc0\xc3\x49\x10\x81.{8}\xff\xd8\xff/s',
798             Groups => { 2 => 'Preview' },
799             Binary => 1,
800             # 0x00 - undef[16]: UUID
801             # 0x10 - int32u[2]: ThumbnailLength
802             # 0x14 - int16u[2]: width/height of image (160/120)
803             RawConv => q{
804             my $len = Get32u(\$val, 0x10);
805             return undef unless length($val) >= $len + 0x18;
806             return substr($val, 0x18, $len);
807             },
808             },
809             # also seen 120-byte record in Garmin MP4's, starting like this (model name at byte 9):
810             # 0000: 47 52 4d 4e 00 00 00 01 00 44 43 35 37 00 00 00 [GRMN.....DC57...]
811             # 0000: 47 52 4d 4e 00 00 00 01 00 44 43 36 36 57 00 00 [GRMN.....DC66W..]
812             # and this in Garmin, followed by 8 bytes of 0's:
813             # 0000: db 11 98 3d 8f 65 43 8c bb b8 e1 ac 56 fe 6b 04
814             { #8
815             Name => 'UUID-Unknown',
816             %unknownInfo,
817             },
818             ],
819             _htc => {
820             Name => 'HTCInfo',
821             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HTCInfo' },
822             },
823             udta => [{
824             Name => 'KenwoodData',
825             Condition => '$$valPt =~ /^VIDEOUUUUUUUUUUUUUUUUUUUUUU/',
826             SubDirectory => {
827             TagTable => 'Image::ExifTool::QuickTime::Stream',
828             ProcessProc => \&ProcessKenwood,
829             },
830             },{
831             Name => 'LigoJSON',
832             Condition => '$$valPt =~ /^LIGOGPSINFO \{/',
833             SubDirectory => {
834             TagTable => 'Image::ExifTool::QuickTime::Stream',
835             ProcessProc => 'Image::ExifTool::LigoGPS::ProcessLigoJSON',
836             },
837             },{
838             Name => 'GKUData',
839             Condition => '$$valPt =~ /^.{8}__V35AX_QVDATA__/',
840             SubDirectory => {
841             TagTable => 'Image::ExifTool::QuickTime::Stream',
842             ProcessProc => 'Image::ExifTool::LigoGPS::ProcessGKU',
843             },
844             },{
845             Name => 'FLIRData',
846             SubDirectory => { TagTable => 'Image::ExifTool::FLIR::UserData' },
847             }],
848             thum => { #PH
849             Name => 'ThumbnailImage',
850             Groups => { 2 => 'Preview' },
851             Binary => 1,
852             },
853             'thm ' => { #PH (70mai A800)
854             Name => 'ThumbnailImage',
855             Groups => { 2 => 'Preview' },
856             Binary => 1,
857             },
858             ardt => { #PH
859             Name => 'ARDroneFile',
860             ValueConv => 'length($val) > 4 ? substr($val,4) : $val', # remove length
861             },
862             prrt => { #PH
863             Name => 'ARDroneTelemetry',
864             Notes => q{
865             telemetry information for each video frame: status1, status2, time, pitch,
866             roll, yaw, speed, altitude
867             },
868             ValueConv => q{
869             my $size = length $val;
870             return \$val if $size < 12 or not $$self{OPTIONS}{Binary};
871             my $len = Get16u(\$val, 2);
872             my $str = '';
873             SetByteOrder('II');
874             my $pos = 12;
875             while ($pos + $len <= $size) {
876             my $s1 = Get16u(\$val, $pos);
877             # s2: 7=take-off?, 3=moving, 4=hovering, 9=landing?, 2=landed
878             my $s2 = Get16u(\$val, $pos + 2);
879             $str .= "$s1 $s2";
880             my $num = int(($len-4)/4);
881             my ($i, $v);
882             for ($i=0; $i<$num; ++$i) {
883             my $pt = $pos + 4 + $i * 4;
884             if ($i > 0 && $i < 4) {
885             $v = GetFloat(\$val, $pt); # pitch/roll/yaw
886             } else {
887             $v = Get32u(\$val, $pt);
888             # convert time to sec, and speed(NC)/altitude to metres
889             $v /= 1000 if $i <= 5;
890             }
891             $str .= " $v";
892             }
893             $str .= "\n";
894             $pos += $len;
895             }
896             SetByteOrder('MM');
897             return \$str;
898             },
899             },
900             udat => { #PH (GPS NMEA-format log written by Datakam Player software)
901             Name => 'GPSLog',
902             Binary => 1, # (actually ASCII, but very lengthy)
903             Notes => 'parsed to extract GPS separately when ExtractEmbedded is used',
904             RawConv => q{
905             $val =~ s/\0+$//; # remove trailing nulls
906             if (length $val and $$self{OPTIONS}{ExtractEmbedded}) {
907             my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
908             Image::ExifTool::QuickTime::ProcessGPSLog($self, { DataPt => \$val }, $tagTbl);
909             }
910             return $val;
911             },
912             },
913             # meta - proprietary XML information written by some Flip cameras - PH
914             # beam - 16 bytes found in an iPhone video
915             IDIT => { #PH (written by DuDuBell M1, VSYS M6L dashcams)
916             Name => 'DateTimeOriginal',
917             Description => 'Date/Time Original',
918             Groups => { 2 => 'Time' },
919             Format => 'string', # (removes trailing "\0")
920             Shift => 'Time',
921             Writable => 1,
922             Permanent => 1,
923             DelValue => '0000-00-00T00:00:00+0000',
924             ValueConv => '$val=~tr/-/:/; $val',
925             ValueConvInv => '$val=~s/(\d+):(\d+):/$1-$2-/; $val',
926             PrintConv => '$self->ConvertDateTime($val)',
927             PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
928             },
929             gps0 => { #PH (DuDuBell M1, VSYS M6L)
930             Name => 'GPSTrack',
931             SubDirectory => {
932             TagTable => 'Image::ExifTool::QuickTime::Stream',
933             ProcessProc => \&Process_gps0,
934             },
935             },
936             gsen => { #PH (DuDuBell M1, VSYS M6L)
937             Name => 'GSensor',
938             SubDirectory => {
939             TagTable => 'Image::ExifTool::QuickTime::Stream',
940             ProcessProc => \&Process_gsen,
941             },
942             },
943             # gpsa - seen hex "01 20 00 00" (DuDuBell M1, VSYS M6L)
944             # gsea - 20 bytes hex "05 00's..." (DuDuBell M1) "05 08 02 01 ..." (VSYS M6L)
945             gdat => { # Base64-encoded JSON-format timed GPS (Nextbase software)
946             Name => 'GPSData',
947             SubDirectory => {
948             TagTable => 'Image::ExifTool::QuickTime::Stream',
949             ProcessProc => \&Process_gdat,
950             },
951             },
952             nbmt => { # (Nextbase)
953             Name => 'NextbaseMeta',
954             SubDirectory => {
955             TagTable => 'Image::ExifTool::QuickTime::Stream',
956             ProcessProc => \&Process_nbmt,
957             },
958             },
959             'GPS ' => { # GPS data written by 70mai dashcam (parsed in QuickTimeStream.pl)
960             Name => 'GPSDataList2',
961             Unknown => 1,
962             Binary => 1,
963             },
964             sefd => {
965             Name => 'SamsungTrailer',
966             SubDirectory => { TagTable => 'Image::ExifTool::Samsung::Trailer' },
967             },
968             # 'samn'? - seen in Vantrue N2S sample video
969             mpvd => {
970             Name => 'MotionPhotoVideo',
971             Notes => 'MP4-format video saved in Samsung motion-photo HEIC images.',
972             Binary => 1,
973             # note that this may be written and/or deleted, but can't currently be added back again
974             Writable => 'undef',
975             WriteLast => 1, # (must come after mdat according to https://developer.android.com/media/platform/motion-photo-format)
976             },
977             # '35AX'? - seen "AT" (Yada RoadCam Pro 4K dashcam)
978             cust => 'CustomInfo', # 70mai A810
979             SEAL => {
980             Name => 'SEAL',
981             SubDirectory => { TagTable => 'Image::ExifTool::XMP::SEAL' },
982             },
983             inst => {
984             Name => 'Insta360Info',
985             DontRead => 1, # don't read into memory when extracting
986             WriteLast => 1, # must go at end of file
987             SubDirectory => {
988             TagTable => 'Image::ExifTool::QuickTime::Stream',
989             ProcessProc => \&ProcessInsta360,
990             },
991             },
992             # Kandao tags (Kandao QooCam 3 Ultra)
993             kvar => {
994             Name => 'KVAR',
995             BlockExtract => 1,
996             Notes => q{
997             by default, data in this tag is parsed to extract some embedded metadata,
998             but it may also be extracted as a KVAR file via the "KVAR" tag or by setting
999             the API BlockExtract option
1000             },
1001             SubDirectory => { TagTable => 'Image::ExifTool::Kandao::Main' },
1002             },
1003             kfix => {
1004             Name => 'KFIX',
1005             SubDirectory => { TagTable => 'Image::ExifTool::Kandao::Main' },
1006             },
1007             kstb => { # (NC)
1008             Name => 'KSTB',
1009             SubDirectory => { TagTable => 'Image::ExifTool::Kandao::Main' },
1010             },
1011             );
1012              
1013             # stuff seen in 'skip' atom (70mai Pro Plus+ MP4 videos)
1014             %Image::ExifTool::QuickTime::SkipInfo = (
1015             PROCESS_PROC => \&ProcessMOV,
1016             GROUPS => { 2 => 'Video' },
1017             'ver ' => 'Version',
1018             # tima - int32u: seen 0x3c
1019             thma => {
1020             Name => 'ThumbnailImage',
1021             Groups => { 2 => 'Preview' },
1022             Binary => 1,
1023             },
1024             );
1025              
1026             # MPEG-4 'ftyp' atom
1027             # (ref http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html)
1028             %Image::ExifTool::QuickTime::FileType = (
1029             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1030             GROUPS => { 2 => 'Video' },
1031             FORMAT => 'int32u',
1032             0 => {
1033             Name => 'MajorBrand',
1034             Format => 'undef[4]',
1035             PrintConv => \%ftypLookup,
1036             },
1037             1 => {
1038             Name => 'MinorVersion',
1039             Format => 'undef[4]',
1040             ValueConv => 'sprintf("%x.%x.%x", unpack("nCC", $val))',
1041             },
1042             2 => {
1043             Name => 'CompatibleBrands',
1044             Format => 'undef[$size-8]',
1045             List => 1, # (for documentation only)
1046             # ignore any entry with a null, and return others as a list
1047             ValueConv => 'my @a=($val=~/.{4}/sg); @a=grep(!/\0/,@a); \@a',
1048             },
1049             );
1050              
1051             # proprietary HTC atom (HTC One MP4 video)
1052             %Image::ExifTool::QuickTime::HTCInfo = (
1053             PROCESS_PROC => \&ProcessMOV,
1054             GROUPS => { 2 => 'Video' },
1055             NOTES => 'Tags written by some HTC camera phones.',
1056             slmt => {
1057             Name => 'Unknown_slmt',
1058             Unknown => 1,
1059             Format => 'int32u', # (observed values: 4)
1060             },
1061             );
1062              
1063             # atoms used in QTIF files
1064             %Image::ExifTool::QuickTime::ImageFile = (
1065             PROCESS_PROC => \&ProcessMOV,
1066             GROUPS => { 2 => 'Image' },
1067             NOTES => 'Tags used in QTIF QuickTime Image Files.',
1068             idsc => {
1069             Name => 'ImageDescription',
1070             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::VisualSampleDesc' },
1071             },
1072             idat => {
1073             Name => 'ImageData',
1074             Binary => 1,
1075             },
1076             iicc => {
1077             Name => 'ICC_Profile',
1078             SubDirectory => { TagTable => 'Image::ExifTool::ICC_Profile::Main' },
1079             },
1080             );
1081              
1082             # 'sv3d' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1083             %Image::ExifTool::QuickTime::sv3d = (
1084             PROCESS_PROC => \&ProcessMOV,
1085             GROUPS => { 2 => 'Video' },
1086             NOTES => q{
1087             Tags defined by the Spherical Video V2 specification. See
1088             L
1089             for the specification.
1090             },
1091             svhd => {
1092             Name => 'MetadataSource',
1093             Format => 'undef',
1094             ValueConv => '$val=~tr/\0//d; $val', # (remove version/flags? and terminator?)
1095             },
1096             proj => {
1097             Name => 'Projection',
1098             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::proj' },
1099             },
1100             );
1101              
1102             # 'proj' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1103             %Image::ExifTool::QuickTime::proj = (
1104             PROCESS_PROC => \&ProcessMOV,
1105             GROUPS => { 2 => 'Video' },
1106             prhd => {
1107             Name => 'ProjectionHeader',
1108             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::prhd' },
1109             },
1110             cbmp => {
1111             Name => 'CubemapProj',
1112             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::cbmp' },
1113             },
1114             equi => {
1115             Name => 'EquirectangularProj',
1116             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::equi' },
1117             },
1118             # mshp - MeshProjection (P.I.T.A. to decode, for not much reward, see ref)
1119             );
1120              
1121             # 'prhd' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1122             %Image::ExifTool::QuickTime::prhd = (
1123             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1124             GROUPS => { 2 => 'Video' },
1125             FORMAT => 'fixed32s',
1126             # 0 - version (high 8 bits) / flags (low 24 bits)
1127             1 => 'PoseYawDegrees',
1128             2 => 'PosePitchDegrees',
1129             3 => 'PoseRollDegrees',
1130             );
1131              
1132             # 'cbmp' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1133             %Image::ExifTool::QuickTime::cbmp = (
1134             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1135             GROUPS => { 2 => 'Video' },
1136             FORMAT => 'int32u',
1137             # 0 - version (high 8 bits) / flags (low 24 bits)
1138             1 => 'Layout',
1139             2 => 'Padding',
1140             );
1141              
1142             # 'equi' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1143             %Image::ExifTool::QuickTime::equi = (
1144             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1145             GROUPS => { 2 => 'Video' },
1146             FORMAT => 'int32u', # (actually 0.32 fixed point)
1147             # 0 - version (high 8 bits) / flags (low 24 bits)
1148             1 => { Name => 'ProjectionBoundsTop', ValueConv => '$val / 4294967296' },
1149             2 => { Name => 'ProjectionBoundsBottom',ValueConv => '$val / 4294967296' },
1150             3 => { Name => 'ProjectionBoundsLeft', ValueConv => '$val / 4294967296' },
1151             4 => { Name => 'ProjectionBoundsRight', ValueConv => '$val / 4294967296' },
1152             );
1153              
1154             # 'btrt' atom information (ref http://lists.freedesktop.org/archives/gstreamer-commits/2011-October/054459.html)
1155             %Image::ExifTool::QuickTime::Bitrate = (
1156             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1157             GROUPS => { 2 => 'Video' },
1158             FORMAT => 'int32u',
1159             PRIORITY => 0, # often filled with zeros
1160             0 => 'BufferSize',
1161             1 => 'MaxBitrate',
1162             2 => 'AverageBitrate',
1163             );
1164              
1165             # 'clap' atom information (ref https://developer.apple.com/library/mac/technotes/tn2162/_index.html#//apple_ref/doc/uid/DTS40013070-CH1-TNTAG9)
1166             %Image::ExifTool::QuickTime::CleanAperture = (
1167             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1168             GROUPS => { 2 => 'Video' },
1169             FORMAT => 'rational64s',
1170             0 => 'CleanApertureWidth',
1171             1 => 'CleanApertureHeight',
1172             2 => 'CleanApertureOffsetX',
1173             3 => 'CleanApertureOffsetY',
1174             );
1175              
1176             # preview data block
1177             %Image::ExifTool::QuickTime::Preview = (
1178             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1179             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
1180             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
1181             GROUPS => { 2 => 'Image' },
1182             FORMAT => 'int16u',
1183             0 => {
1184             Name => 'PreviewDate',
1185             Format => 'int32u',
1186             Groups => { 2 => 'Time' },
1187             %timeInfo,
1188             },
1189             2 => 'PreviewVersion',
1190             3 => {
1191             Name => 'PreviewAtomType',
1192             Format => 'string[4]',
1193             },
1194             5 => 'PreviewAtomIndex',
1195             );
1196              
1197             # movie atoms
1198             %Image::ExifTool::QuickTime::Movie = (
1199             PROCESS_PROC => \&ProcessMOV,
1200             WRITE_PROC => \&WriteQuickTime,
1201             GROUPS => { 2 => 'Video' },
1202             mvhd => {
1203             Name => 'MovieHeader',
1204             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MovieHeader' },
1205             },
1206             trak => {
1207             Name => 'Track',
1208             CanCreate => 0, # don't create this atom
1209             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Track' },
1210             },
1211             udta => {
1212             Name => 'UserData',
1213             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::UserData' },
1214             },
1215             meta => { # 'meta' is found here in my EX-F1 MOV sample - PH
1216             Name => 'Meta',
1217             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1218             },
1219             iods => {
1220             Name => 'InitialObjectDescriptor',
1221             Flags => ['Binary','Unknown'],
1222             },
1223             uuid => [
1224             { #11 (MP4 files) (also found in QuickTime::Track)
1225             Name => 'UUID-USMT',
1226             Condition => '$$valPt=~/^USMT!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
1227             SubDirectory => {
1228             TagTable => 'Image::ExifTool::QuickTime::UserMedia',
1229             Start => 16,
1230             },
1231             },
1232             { #PH (Canon SX280)
1233             Name => 'UUID-Canon',
1234             Condition => '$$valPt=~/^\x85\xc0\xb6\x87\x82\x0f\x11\xe0\x81\x11\xf4\xce\x46\x2b\x6a\x48/',
1235             SubDirectory => {
1236             TagTable => 'Image::ExifTool::Canon::uuid',
1237             Start => 16,
1238             },
1239             },
1240             {
1241             Name => 'GarminGPS',
1242             Condition => q{
1243             $$valPt=~/^\x9b\x63\x0f\x8d\x63\x74\x40\xec\x82\x04\xbc\x5f\xf5\x09\x17\x28/ and
1244             $$self{OPTIONS}{ExtractEmbedded}
1245             },
1246             SubDirectory => {
1247             TagTable => 'Image::ExifTool::QuickTime::Stream',
1248             ProcessProc => \&ProcessGarminGPS,
1249             },
1250             },
1251             {
1252             Name => 'GarminGPS',
1253             Condition => '$$valPt=~/^\x9b\x63\x0f\x8d\x63\x74\x40\xec\x82\x04\xbc\x5f\xf5\x09\x17\x28/',
1254             Notes => 'Garmin GPS sensor data',
1255             RawConv => q{
1256             $self->Warn('Use the ExtractEmbedded option to decode timed Garmin GPS',3);
1257             return \$val;
1258             },
1259             },
1260             {
1261             Name => 'UUID-Unknown',
1262             %unknownInfo,
1263             },
1264             ],
1265             cmov => {
1266             Name => 'CompressedMovie',
1267             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::CMovie' },
1268             },
1269             htka => { # (written by HTC One M8 in slow-motion 1280x720 video - PH)
1270             Name => 'HTCTrack',
1271             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Track' },
1272             },
1273             'gps ' => { # GPS data written by Novatek cameras (parsed in QuickTimeStream.pl)
1274             Name => 'GPSDataList',
1275             Unknown => 1,
1276             Binary => 1,
1277             },
1278             meco => { #ISO14496-12:2015
1279             Name => 'OtherMeta',
1280             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::OtherMeta' },
1281             },
1282             # prfl - Profile (ref 12)
1283             # clip - clipping --> contains crgn (clip region) (ref 12)
1284             # mvex - movie extends --> contains mehd (movie extends header), trex (track extends) (ref 14)
1285             # ICAT - 4 bytes: "6350" (Nikon CoolPix S6900), "6500" (Panasonic FT7)
1286             # ctab - color table (ref 29)
1287             );
1288              
1289             # (ref CFFMediaFormat-2_1.pdf)
1290             %Image::ExifTool::QuickTime::MovieFragment = (
1291             PROCESS_PROC => \&ProcessMOV,
1292             WRITE_PROC => \&WriteQuickTime,
1293             GROUPS => { 2 => 'Video' },
1294             mfhd => {
1295             Name => 'MovieFragmentHeader',
1296             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MovieFragHdr' },
1297             },
1298             traf => {
1299             Name => 'TrackFragment',
1300             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackFragment' },
1301             },
1302             meta => { #ISO14496-12:2015
1303             Name => 'Meta',
1304             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1305             },
1306             );
1307              
1308             # (ref CFFMediaFormat-2_1.pdf)
1309             %Image::ExifTool::QuickTime::MovieFragHdr = (
1310             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1311             GROUPS => { 2 => 'Video' },
1312             FORMAT => 'int32u',
1313             1 => 'MovieFragmentSequence',
1314             );
1315              
1316             # (ref CFFMediaFormat-2_1.pdf)
1317             %Image::ExifTool::QuickTime::TrackFragment = (
1318             PROCESS_PROC => \&ProcessMOV,
1319             WRITE_PROC => \&WriteQuickTime,
1320             GROUPS => { 2 => 'Video' },
1321             meta => { #ISO14496-12:2015
1322             Name => 'Meta',
1323             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1324             },
1325             # tfhd - track fragment header
1326             # edts - edits --> contains elst (edit list) (ref PH)
1327             # tfdt - track fragment base media decode time
1328             # trik - trick play box
1329             # trun - track fragment run box
1330             # avcn - AVC NAL unit storage box
1331             # secn - sample encryption box
1332             # saio - sample auxiliary information offsets box
1333             # sbgp - sample to group box
1334             # sgpd - sample group description box
1335             # sdtp - independent and disposable samples (ref 5)
1336             # subs - sub-sample information (ref 5)
1337             );
1338              
1339             # movie header data block
1340             %Image::ExifTool::QuickTime::MovieHeader = (
1341             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1342             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
1343             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
1344             GROUPS => { 2 => 'Video' },
1345             FORMAT => 'int32u',
1346             DATAMEMBER => [ 0, 1, 2, 3, 4 ],
1347             0 => {
1348             Name => 'MovieHeaderVersion',
1349             Format => 'int8u',
1350             RawConv => '$$self{MovieHeaderVersion} = $val',
1351             },
1352             1 => {
1353             Name => 'CreateDate',
1354             Groups => { 2 => 'Time' },
1355             %timeInfo,
1356             RawConv => q{
1357             if ($val) {
1358             my $offset = (66 * 365 + 17) * 24 * 3600;
1359             if ($val >= $offset or $$self{OPTIONS}{QuickTimeUTC}) {
1360             $val -= $offset;
1361             } elsif (not $$self{IsWriting}) {
1362             $self->Warn('Patched incorrect time zero for QuickTime date/time tag',1);
1363             }
1364             } else {
1365             undef $val if $$self{OPTIONS}{StrictDate};
1366             }
1367             return $$self{CreateDate} = $val;
1368             },
1369             # this is int64u if MovieHeaderVersion == 1 (ref 13)
1370             Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
1371             },
1372             2 => {
1373             Name => 'ModifyDate',
1374             Groups => { 2 => 'Time' },
1375             %timeInfo,
1376             # this is int64u if MovieHeaderVersion == 1 (ref 13)
1377             Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
1378             },
1379             3 => {
1380             Name => 'TimeScale',
1381             RawConv => '$$self{TimeScale} = $val',
1382             },
1383             4 => {
1384             Name => 'Duration',
1385             %durationInfo,
1386             # this is int64u if MovieHeaderVersion == 1 (ref 13)
1387             Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
1388             # (Note: this Duration seems to be the time of the key frame in
1389             # the NRT Metadata track of iPhone live-photo MOV videos)
1390             },
1391             5 => {
1392             Name => 'PreferredRate',
1393             ValueConv => '$val / 0x10000',
1394             },
1395             6 => {
1396             Name => 'PreferredVolume',
1397             Format => 'int16u',
1398             ValueConv => '$val / 256',
1399             PrintConv => 'sprintf("%.2f%%", $val * 100)',
1400             },
1401             9 => {
1402             Name => 'MatrixStructure',
1403             Format => 'fixed32s[9]',
1404             # (the right column is fixed 2.30 instead of 16.16)
1405             ValueConv => q{
1406             my @a = split ' ',$val;
1407             $_ /= 0x4000 foreach @a[2,5,8];
1408             return "@a";
1409             },
1410             },
1411             18 => { Name => 'PreviewTime', %durationInfo },
1412             19 => { Name => 'PreviewDuration', %durationInfo },
1413             20 => { Name => 'PosterTime', %durationInfo },
1414             21 => { Name => 'SelectionTime', %durationInfo },
1415             22 => { Name => 'SelectionDuration',%durationInfo },
1416             23 => { Name => 'CurrentTime', %durationInfo },
1417             24 => 'NextTrackID',
1418             );
1419              
1420             # track atoms
1421             %Image::ExifTool::QuickTime::Track = (
1422             PROCESS_PROC => \&ProcessMOV,
1423             WRITE_PROC => \&WriteQuickTime,
1424             GROUPS => { 1 => 'Track#', 2 => 'Video' },
1425             tkhd => {
1426             Name => 'TrackHeader',
1427             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackHeader' },
1428             },
1429             udta => {
1430             Name => 'UserData',
1431             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::UserData' },
1432             },
1433             mdia => { #MP4
1434             Name => 'Media',
1435             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Media' },
1436             },
1437             meta => { #PH (MOV)
1438             Name => 'Meta',
1439             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1440             },
1441             tref => {
1442             Name => 'TrackRef',
1443             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackRef' },
1444             },
1445             tapt => {
1446             Name => 'TrackAperture',
1447             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackAperture' },
1448             },
1449             uuid => [
1450             { #11 (MP4 files) (also found in QuickTime::Movie)
1451             Name => 'UUID-USMT',
1452             Condition => '$$valPt=~/^USMT!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
1453             SubDirectory => {
1454             TagTable => 'Image::ExifTool::QuickTime::UserMedia',
1455             Start => 16,
1456             },
1457             },
1458             { #https://github.com/google/spatial-media/blob/master/docs/spherical-video-rfc.md
1459             Name => 'SphericalVideoXML',
1460             # (this tag is readable/writable as a block through the Extra SphericalVideoXML tags)
1461             Condition => '$$valPt=~/^\xff\xcc\x82\x63\xf8\x55\x4a\x93\x88\x14\x58\x7a\x02\x52\x1f\xdd/',
1462             WriteGroup => 'GSpherical', # write only GSpherical XMP tags here
1463             MediaType => 'vide', # only write in video tracks
1464             SubDirectory => {
1465             TagTable => 'Image::ExifTool::XMP::Main',
1466             Start => 16,
1467             ProcessProc => 'Image::ExifTool::XMP::ProcessGSpherical',
1468             WriteProc => 'Image::ExifTool::XMP::WriteGSpherical',
1469             },
1470             },
1471             {
1472             Name => 'UUID-Unknown',
1473             %unknownInfo,
1474             },
1475             ],
1476             meco => { #ISO14492-12:2015 pg 83
1477             Name => 'OtherMeta',
1478             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::OtherMeta' },
1479             },
1480             # edts - edits --> contains elst (edit list)
1481             # clip - clipping --> contains crgn (clip region)
1482             # matt - track matt --> contains kmat (compressed matt)
1483             # load - track loading settings
1484             # imap - track input map --> contains ' in' --> contains ' ty', obid
1485             # prfl - Profile (ref 12)
1486             # txas - track exclude from autoselection (ref 29)
1487             );
1488              
1489             # track header data block
1490             %Image::ExifTool::QuickTime::TrackHeader = (
1491             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1492             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
1493             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
1494             GROUPS => { 1 => 'Track#', 2 => 'Video' },
1495             FORMAT => 'int32u',
1496             DATAMEMBER => [ 0, 1, 2, 5, 7 ],
1497             0 => {
1498             Name => 'TrackHeaderVersion',
1499             Format => 'int8u',
1500             Priority => 0,
1501             RawConv => '$$self{TrackHeaderVersion} = $val',
1502             },
1503             1 => {
1504             Name => 'TrackCreateDate',
1505             Priority => 0,
1506             Groups => { 2 => 'Time' },
1507             %timeInfo,
1508             # this is int64u if TrackHeaderVersion == 1 (ref 13)
1509             Hook => '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
1510             },
1511             2 => {
1512             Name => 'TrackModifyDate',
1513             Priority => 0,
1514             Groups => { 2 => 'Time' },
1515             %timeInfo,
1516             # this is int64u if TrackHeaderVersion == 1 (ref 13)
1517             Hook => '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
1518             },
1519             3 => {
1520             Name => 'TrackID',
1521             Priority => 0,
1522             },
1523             5 => {
1524             Name => 'TrackDuration',
1525             Priority => 0,
1526             %durationInfo,
1527             # this is int64u if TrackHeaderVersion == 1 (ref 13)
1528             Hook => '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
1529             },
1530             7 => { # (used only for writing MatrixStructure)
1531             Name => 'ImageSizeLookahead',
1532             Hidden => 1,
1533             Format => 'int32u[14]',
1534             RawConv => '$$self{ImageSizeLookahead} = $val; undef',
1535             },
1536             8 => {
1537             Name => 'TrackLayer',
1538             Format => 'int16u',
1539             Priority => 0,
1540             },
1541             9 => {
1542             Name => 'TrackVolume',
1543             Format => 'int16u',
1544             Priority => 0,
1545             ValueConv => '$val / 256',
1546             PrintConv => 'sprintf("%.2f%%", $val * 100)',
1547             },
1548             10 => {
1549             Name => 'MatrixStructure',
1550             Format => 'fixed32s[9]',
1551             Notes => 'writable for the video track via the Composite Rotation tag',
1552             Writable => 1,
1553             Protected => 1,
1554             Permanent => 1,
1555             # only set rotation if image size is non-zero
1556             RawConvInv => \&GetMatrixStructure,
1557             # (the right column is fixed 2.30 instead of 16.16)
1558             ValueConv => q{
1559             my @a = split ' ',$val;
1560             $_ /= 0x4000 foreach @a[2,5,8];
1561             return "@a";
1562             },
1563             ValueConvInv => q{
1564             my @a = split ' ',$val;
1565             $_ *= 0x4000 foreach @a[2,5,8];
1566             return "@a";
1567             },
1568             },
1569             19 => {
1570             Name => 'ImageWidth',
1571             Priority => 0,
1572             RawConv => \&FixWrongFormat,
1573             },
1574             20 => {
1575             Name => 'ImageHeight',
1576             Priority => 0,
1577             RawConv => \&FixWrongFormat,
1578             },
1579             );
1580              
1581             # user data atoms
1582             %Image::ExifTool::QuickTime::UserData = (
1583             PROCESS_PROC => \&ProcessMOV,
1584             WRITE_PROC => \&WriteQuickTime,
1585             CHECK_PROC => \&CheckQTValue,
1586             GROUPS => { 1 => 'UserData', 2 => 'Video' },
1587             WRITABLE => 1,
1588             PREFERRED => 1, # (preferred over Keys tags when writing)
1589             FORMAT => 'string',
1590             WRITE_GROUP => 'UserData',
1591             LANG_INFO => \&GetLangInfo,
1592             NOTES => q{
1593             Tag ID's beginning with the copyright symbol (hex 0xa9) are multi-language
1594             text. Alternate language tags are accessed by adding a dash followed by a
1595             3-character ISO 639-2 language code to the tag name. ExifTool will extract
1596             any multi-language user data tags found, even if they aren't in this table.
1597             Note when creating new tags,
1598             L tags are
1599             preferred over these, so to create the tag when a same-named ItemList tag
1600             exists, either "UserData" must be specified (eg. C<-UserData:Artist=Monet>
1601             on the command line), or the PREFERRED level must be changed via
1602             L.
1603             },
1604             "\xa9cpy" => { Name => 'Copyright', Groups => { 2 => 'Author' } },
1605             "\xa9day" => {
1606             Name => 'ContentCreateDate',
1607             Groups => { 2 => 'Time' },
1608             %iso8601Date,
1609             },
1610             "\xa9ART" => 'Artist', #PH (iTunes 8.0.2)
1611             "\xa9alb" => 'Album', #PH (iTunes 8.0.2)
1612             "\xa9arg" => 'Arranger', #12
1613             "\xa9ark" => 'ArrangerKeywords', #12
1614             "\xa9cmt" => 'Comment', #PH (iTunes 8.0.2)
1615             "\xa9cok" => 'ComposerKeywords', #12
1616             "\xa9com" => 'Composer', #12
1617             "\xa9dir" => 'Director', #12
1618             "\xa9ed1" => 'Edit1',
1619             "\xa9ed2" => 'Edit2',
1620             "\xa9ed3" => 'Edit3',
1621             "\xa9ed4" => 'Edit4',
1622             "\xa9ed5" => 'Edit5',
1623             "\xa9ed6" => 'Edit6',
1624             "\xa9ed7" => 'Edit7',
1625             "\xa9ed8" => 'Edit8',
1626             "\xa9ed9" => 'Edit9',
1627             "\xa9fmt" => 'Format',
1628             "\xa9gen" => 'Genre', #PH (iTunes 8.0.2)
1629             "\xa9grp" => 'Grouping', #PH (NC)
1630             "\xa9inf" => 'Information',
1631             "\xa9isr" => 'ISRCCode', #12
1632             "\xa9lab" => 'RecordLabelName', #12
1633             "\xa9lal" => 'RecordLabelURL', #12
1634             "\xa9lyr" => 'Lyrics', #PH (NC)
1635             "\xa9mak" => 'Make', #12
1636             "\xa9mal" => 'MakerURL', #12
1637             "\xa9mod" => 'Model', #PH
1638             "\xa9nam" => 'Title', #12
1639             "\xa9pdk" => 'ProducerKeywords', #12
1640             "\xa9phg" => 'RecordingCopyright', #12
1641             "\xa9prd" => 'Producer',
1642             "\xa9prf" => 'Performers',
1643             "\xa9prk" => 'PerformerKeywords', #12
1644             "\xa9prl" => 'PerformerURL',
1645             "\xa9req" => 'Requirements',
1646             "\xa9snk" => 'SubtitleKeywords', #12
1647             "\xa9snm" => 'Subtitle', #12
1648             "\xa9src" => 'SourceCredits', #12
1649             "\xa9swf" => 'SongWriter', #12
1650             "\xa9swk" => 'SongWriterKeywords', #12
1651             "\xa9swr" => 'SoftwareVersion', #12
1652             "\xa9too" => 'Encoder', #PH (NC)
1653             "\xa9trk" => 'Track', #PH (NC)
1654             "\xa9wrt" => { Name => 'Composer', Avoid => 1 }, # ("\xa9com" is preferred in UserData)
1655             "\xa9xyz" => { #PH (iPhone 3GS)
1656             Name => 'GPSCoordinates',
1657             Groups => { 2 => 'Location' },
1658             ValueConv => \&ConvertISO6709,
1659             ValueConvInv => \&ConvInvISO6709,
1660             PrintConv => \&PrintGPSCoordinates,
1661             PrintConvInv => \&PrintInvGPSCoordinates,
1662             },
1663             name => 'Name',
1664             WLOC => {
1665             Name => 'WindowLocation',
1666             Format => 'int16u',
1667             },
1668             LOOP => {
1669             Name => 'LoopStyle',
1670             Format => 'int32u',
1671             PrintConv => {
1672             1 => 'Normal',
1673             2 => 'Palindromic',
1674             },
1675             },
1676             SelO => {
1677             Name => 'PlaySelection',
1678             Format => 'int8u',
1679             },
1680             AllF => {
1681             Name => 'PlayAllFrames',
1682             Format => 'int8u',
1683             },
1684             meta => {
1685             Name => 'Meta',
1686             SubDirectory => {
1687             TagTable => 'Image::ExifTool::QuickTime::Meta',
1688             Start => 4, # must skip 4-byte version number header
1689             },
1690             },
1691             tnam => { #29 (NC)
1692             Name => 'TrackName',
1693             IText => 4,
1694             },
1695             'ptv '=> {
1696             Name => 'PrintToVideo',
1697             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Video' },
1698             },
1699             hnti => {
1700             Name => 'HintInfo',
1701             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HintInfo' },
1702             },
1703             hinf => {
1704             Name => 'HintTrackInfo',
1705             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HintTrackInfo' },
1706             },
1707             hinv => 'HintVersion', #PH (guess)
1708             XMP_ => { #PH (Adobe CS3 Bridge)
1709             Name => 'XMP',
1710             WriteGroup => 'XMP', # (write main tags here)
1711             # *** this is where ExifTool writes XMP in MOV videos (as per XMP spec) ***
1712             SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
1713             },
1714             # the following are 3gp tags, references:
1715             # http://atomicparsley.sourceforge.net
1716             # http://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_25/Docs/
1717             # (note that all %langText3gp tags are Avoid => 1)
1718             cprt => { Name => 'Copyright', %langText3gp, Groups => { 2 => 'Author' } },
1719             auth => { Name => 'Author', %langText3gp, Groups => { 2 => 'Author' } },
1720             titl => { Name => 'Title', %langText3gp },
1721             dscp => { Name => 'Description',%langText3gp },
1722             perf => { Name => 'Performer', %langText3gp },
1723             gnre => { Name => 'Genre', %langText3gp },
1724             albm => { Name => 'Album', %langText3gp },
1725             coll => { Name => 'CollectionName', %langText3gp }, #17
1726             rtng => {
1727             Name => 'Rating',
1728             Writable => 'undef',
1729             Avoid => 1,
1730             # (4-byte flags, 4-char entity, 4-char criteria, 2-byte lang, string)
1731             IText => 14, # (14 bytes before string)
1732             Notes => 'string in the form "Entity=XXXX Criteria=XXXX XXXXX", used in 3gp videos',
1733             ValueConv => '$val=~s/^(.{4})(.{4})/Entity=$1 Criteria=$2 /i; $val',
1734             ValueConvInv => '$val=~s/Entity=(.{4}) Criteria=(.{4}) ?/$1$2/i; $val',
1735             },
1736             clsf => {
1737             Name => 'Classification',
1738             Writable => 'undef',
1739             Avoid => 1,
1740             # (4-byte flags, 4-char entity, 2-byte index, 2-byte lang, string)
1741             IText => 12,
1742             Notes => 'string in the form "Entity=XXXX Index=### XXXXX", used in 3gp videos',
1743             ValueConv => '$val=~s/^(.{4})(.{2})/"Entity=$1 Index=".unpack("n",$2)." "/ie; $val',
1744             ValueConvInv => '$val=~s/Entity=(.{4}) Index=(\d+) ?/$1.pack("n",$2)/ie; $val',
1745             },
1746             kywd => {
1747             Name => 'Keywords',
1748             # (4 byte flags, 2-byte lang, 1-byte count, count x pascal strings, ref 17)
1749             # (but I have also seen a simple string written by iPhone, so don't make writable yet)
1750             Notes => "not writable because Apple doesn't follow the 3gp specification",
1751             RawConv => q{
1752             my $sep = $self->Options('ListSep');
1753             return join($sep, split /\0+/, $val) unless $val =~ /^\0/; # (iPhone)
1754             return '' unless length $val >= 7;
1755             my $lang = Image::ExifTool::QuickTime::UnpackLang(Get16u(\$val, 4));
1756             $lang = $lang ? "($lang) " : '';
1757             my $num = Get8u(\$val, 6);
1758             my ($i, @vals);
1759             my $pos = 7;
1760             for ($i=0; $i<$num; ++$i) {
1761             last if $pos >= length $val;
1762             my $len = Get8u(\$val, $pos++);
1763             last if $pos + $len > length $val;
1764             my $v = substr($val, $pos, $len);
1765             $v = $self->Decode($v, 'UTF16') if $v =~ /^\xfe\xff/;
1766             push @vals, $v;
1767             $pos += $len;
1768             }
1769             return $lang . join($sep, @vals);
1770             },
1771             },
1772             loci => {
1773             Name => 'LocationInformation',
1774             Groups => { 2 => 'Location' },
1775             Writable => 'undef',
1776             IText => 6,
1777             Avoid => 1,
1778             NoDecode => 1, # (we'll decode the data ourself)
1779             Notes => q{
1780             string in the form "XXXXX Role=XXX Lat=XXX Lon=XXX Alt=XXX Body=XXX
1781             Notes=XXX", used in 3gp videos
1782             },
1783             # (4-byte flags, 2-byte lang, location string, 1-byte role, 4-byte fixed longitude,
1784             # 4-byte fixed latitude, 4-byte fixed altitude, body string, notes string)
1785             RawConv => q{
1786             my $str;
1787             if ($val =~ /^\xfe\xff/) {
1788             $val =~ s/^(\xfe\xff(.{2})*?)\0\0//s or return '';
1789             $str = $self->Decode($1, 'UTF16');
1790             } else {
1791             $val =~ s/^(.*?)\0//s or return '';
1792             $str = $self->Decode($1, 'UTF8');
1793             }
1794             $str = '(none)' unless length $str;
1795             return '' if length $val < 13;
1796             my $role = Get8u(\$val, 0);
1797             my $lon = GetFixed32s(\$val, 1);
1798             my $lat = GetFixed32s(\$val, 5);
1799             my $alt = GetFixed32s(\$val, 9);
1800             my $roleStr = {0=>'shooting',1=>'real',2=>'fictional',3=>'reserved'}->{$role};
1801             $str .= ' Role=' . ($roleStr || "unknown($role)");
1802             $str .= sprintf(' Lat=%.5f Lon=%.5f Alt=%.2f', $lat, $lon, $alt);
1803             $val = substr($val, 13);
1804             if ($val =~ s/^(\xfe\xff(.{2})*?)\0\0//s) {
1805             $str .= ' Body=' . $self->Decode($1, 'UTF16');
1806             } elsif ($val =~ s/^(.*?)\0//s) {
1807             $str .= ' Body=' . $self->Decode($1, 'UTF8');
1808             }
1809             if ($val =~ s/^(\xfe\xff(.{2})*?)\0\0//s) {
1810             $str .= ' Notes=' . $self->Decode($1, 'UTF16');
1811             } elsif ($val =~ s/^(.*?)\0//s) {
1812             $str .= ' Notes=' . $self->Decode($1, 'UTF8');
1813             }
1814             return $str;
1815             },
1816             RawConvInv => q{
1817             my ($role, $lat, $lon, $alt, $body, $note);
1818             $lat = $1 if $val =~ s/ Lat=([-+]?[.\d]+)//i;
1819             $lon = $1 if $val =~ s/ Lon=([-+]?[.\d]+)//i;
1820             $alt = $1 if $val =~ s/ Alt=([-+]?[.\d]+)//i;
1821             $note = $val =~ s/ Notes=(.*)//i ? $1 : '';
1822             $body = $val =~ s/ Body=(.*)//i ? $1 : '';
1823             $role = $val =~ s/ Role=(.*)//i ? $1 : '';
1824             $val = '' if $val eq '(none)';
1825             $role = {shooting=>0,real=>1,fictional=>2}->{lc $role} || 0;
1826             return $self->Encode($val, 'UTF8') . "\0" . Set8u($role) .
1827             SetFixed32s(defined $lon ? $lon : 999) .
1828             SetFixed32s(defined $lat ? $lat : 999) .
1829             SetFixed32s(defined $alt ? $alt : 0) .
1830             $self->Encode($body) . "\0" .
1831             $self->Encode($note) . "\0";
1832             },
1833             },
1834             yrrc => {
1835             Name => 'Year',
1836             Writable => 'undef',
1837             Groups => { 2 => 'Time' },
1838             Avoid => 1,
1839             Notes => 'used in 3gp videos',
1840             ValueConv => 'length($val) >= 6 ? unpack("x4n",$val) : ""',
1841             ValueConvInv => 'pack("Nn",0,$val)',
1842             },
1843             urat => { #17
1844             Name => 'UserRating',
1845             Writable => 'undef',
1846             Notes => 'used in 3gp videos',
1847             Avoid => 1,
1848             ValueConv => q{
1849             return '' unless length $val >= 8;
1850             unpack('x7C', $val);
1851             },
1852             ValueConvInv => 'pack("N2",0,$val)',
1853             },
1854             # tsel - TrackSelection (ref 17)
1855             # Apple tags (ref 16[dead] -- see ref 25 instead)
1856             angl => { Name => 'CameraAngle', Format => 'string' }, # (NC)
1857             clfn => { Name => 'ClipFileName', Format => 'string' }, # (NC)
1858             clid => { Name => 'ClipID', Format => 'string' }, # (NC)
1859             cmid => { Name => 'CameraID', Format => 'string' }, # (NC)
1860             cmnm => { # (NC)
1861             Name => 'Model',
1862             Description => 'Camera Model Name',
1863             Avoid => 1,
1864             Format => 'string', # (necessary to remove the trailing NULL)
1865             },
1866             date => {
1867             Name => 'DateTimeOriginal',
1868             Description => 'Date/Time Original',
1869             Groups => { 2 => 'Time' },
1870             Notes => q{
1871             Apple Photos has been reported to show a crazy date/time for some MP4 files
1872             containing this tag, but perhaps only if it is missing a time zone
1873             }, #forum10690/11125
1874             %iso8601Date,
1875             },
1876             manu => { # (SX280)
1877             Name => 'Make',
1878             Avoid => 1,
1879             # (with Canon there are 6 unknown bytes before the model: "\0\0\0\0\x15\xc7")
1880             RawConv => '$val=~s/^\0{4}..//s; $val=~s/\0.*//; $val',
1881             },
1882             modl => { # (Samsung GT-S8530, Canon SX280)
1883             Name => 'Model',
1884             Description => 'Camera Model Name',
1885             Avoid => 1,
1886             # (with Canon there are 6 unknown bytes before the model: "\0\0\0\0\x15\xc7")
1887             RawConv => '$val=~s/^\0{4}..//s; $val=~s/\0.*//; $val',
1888             },
1889             reel => { Name => 'ReelName', Format => 'string' }, # (NC)
1890             scen => { Name => 'Scene', Format => 'string' }, # (NC)
1891             shot => { Name => 'ShotName', Format => 'string' }, # (NC)
1892             slno => { Name => 'SerialNumber', Format => 'string' }, # (NC)
1893             apmd => { Name => 'ApertureMode', Format => 'undef' }, #20
1894             kgtt => { #http://lists.ffmpeg.org/pipermail/ffmpeg-devel-irc/2012-June/000707.html
1895             # 'TrackType' will expand to 'Track#Type' when found inside a track
1896             Name => 'TrackType',
1897             # set flag to process this as international text
1898             # even though the tag ID doesn't start with 0xa9
1899             IText => 4, # IText with 4-byte header
1900             },
1901             chpl => { # (Nero chapter list)
1902             Name => 'ChapterList',
1903             ValueConv => \&ConvertChapterList,
1904             PrintConv => \&PrintChapter,
1905             },
1906             # ndrm - 7 bytes (0 0 0 1 0 0 0) Nero Digital Rights Management? (PH)
1907             # other non-Apple tags (ref 16)
1908             # hpix - HipixRichPicture (ref 16, HIPIX)
1909             # strk - sub-track information (ref 16, ISO)
1910             #
1911             # Manufacturer-specific metadata
1912             #
1913             TAGS => [ #PH
1914             # these tags were initially discovered in a Pentax movie,
1915             # but similar information is found in videos from other manufacturers
1916             {
1917             Name => 'FujiFilmTags',
1918             Condition => '$$valPt =~ /^FUJIFILM DIGITAL CAMERA\0/',
1919             SubDirectory => {
1920             TagTable => 'Image::ExifTool::FujiFilm::MOV',
1921             ByteOrder => 'LittleEndian',
1922             },
1923             },
1924             {
1925             Name => 'KodakTags',
1926             Condition => '$$valPt =~ /^EASTMAN KODAK COMPANY/',
1927             SubDirectory => {
1928             TagTable => 'Image::ExifTool::Kodak::MOV',
1929             ByteOrder => 'LittleEndian',
1930             },
1931             },
1932             {
1933             Name => 'KonicaMinoltaTags',
1934             Condition => '$$valPt =~ /^KONICA MINOLTA DIGITAL CAMERA/',
1935             SubDirectory => {
1936             TagTable => 'Image::ExifTool::Minolta::MOV1',
1937             ByteOrder => 'LittleEndian',
1938             },
1939             },
1940             {
1941             Name => 'MinoltaTags',
1942             Condition => '$$valPt =~ /^MINOLTA DIGITAL CAMERA/',
1943             SubDirectory => {
1944             TagTable => 'Image::ExifTool::Minolta::MOV2',
1945             ByteOrder => 'LittleEndian',
1946             },
1947             },
1948             {
1949             Name => 'NikonTags',
1950             Condition => '$$valPt =~ /^NIKON DIGITAL CAMERA\0/',
1951             SubDirectory => {
1952             TagTable => 'Image::ExifTool::Nikon::MOV',
1953             ByteOrder => 'LittleEndian',
1954             },
1955             },
1956             {
1957             Name => 'OlympusTags1',
1958             Condition => '$$valPt =~ /^OLYMPUS DIGITAL CAMERA\0.{9}\x01\0/s',
1959             SubDirectory => {
1960             TagTable => 'Image::ExifTool::Olympus::MOV1',
1961             ByteOrder => 'LittleEndian',
1962             },
1963             },
1964             {
1965             Name => 'OlympusTags2',
1966             Condition => '$$valPt =~ /^OLYMPUS DIGITAL CAMERA(?!\0.{21}\x0a\0{3})/s',
1967             SubDirectory => {
1968             TagTable => 'Image::ExifTool::Olympus::MOV2',
1969             ByteOrder => 'LittleEndian',
1970             },
1971             },
1972             {
1973             Name => 'OlympusTags3',
1974             Condition => '$$valPt =~ /^OLYMPUS DIGITAL CAMERA\0/',
1975             SubDirectory => {
1976             TagTable => 'Image::ExifTool::Olympus::MP4',
1977             ByteOrder => 'LittleEndian',
1978             },
1979             },
1980             {
1981             Name => 'OlympusTags4',
1982             Condition => '$$valPt =~ /^.{16}OLYM\0/s',
1983             SubDirectory => {
1984             TagTable => 'Image::ExifTool::Olympus::MOV3',
1985             Start => 12,
1986             },
1987             },
1988             {
1989             Name => 'PentaxTags',
1990             Condition => '$$valPt =~ /^PENTAX DIGITAL CAMERA\0/',
1991             SubDirectory => {
1992             TagTable => 'Image::ExifTool::Pentax::MOV',
1993             ByteOrder => 'LittleEndian',
1994             },
1995             },
1996             {
1997             Name => 'SamsungTags',
1998             Condition => '$$valPt =~ /^SAMSUNG DIGITAL CAMERA\0/',
1999             SubDirectory => {
2000             TagTable => 'Image::ExifTool::Samsung::MP4',
2001             ByteOrder => 'LittleEndian',
2002             },
2003             },
2004             {
2005             Name => 'SanyoMOV',
2006             Condition => q{
2007             $$valPt =~ /^SANYO DIGITAL CAMERA\0/ and
2008             $$self{FileType} eq "MOV"
2009             },
2010             SubDirectory => {
2011             TagTable => 'Image::ExifTool::Sanyo::MOV',
2012             ByteOrder => 'LittleEndian',
2013             },
2014             },
2015             {
2016             Name => 'SanyoMP4',
2017             Condition => q{
2018             $$valPt =~ /^SANYO DIGITAL CAMERA\0/ and
2019             $$self{FileType} eq "MP4"
2020             },
2021             SubDirectory => {
2022             TagTable => 'Image::ExifTool::Sanyo::MP4',
2023             ByteOrder => 'LittleEndian',
2024             },
2025             },
2026             {
2027             Name => 'UnknownTags',
2028             Unknown => 1,
2029             Binary => 1
2030             },
2031             ],
2032             # ---- Canon ----
2033             CNCV => { Name => 'CompressorVersion', Format => 'string' }, #PH (5D Mark II)
2034             CNMN => {
2035             Name => 'Model', #PH (EOS 550D)
2036             Description => 'Camera Model Name',
2037             Avoid => 1,
2038             Format => 'string', # (necessary to remove the trailing NULL)
2039             },
2040             CNFV => { Name => 'FirmwareVersion', Format => 'string' }, #PH (EOS 550D)
2041             CNTH => { #PH (PowerShot S95)
2042             Name => 'CanonCNTH',
2043             SubDirectory => { TagTable => 'Image::ExifTool::Canon::CNTH' },
2044             },
2045             CNOP => { #PH (7DmkII)
2046             Name => 'CanonCNOP',
2047             SubDirectory => { TagTable => 'Image::ExifTool::Canon::CNOP' },
2048             },
2049             # CNDB - 2112 bytes (550D)
2050             # CNDM - 4 bytes - 0xff,0xd8,0xff,0xd9 (S95)
2051             # CNDG - 10232 bytes, mostly zeros (N100)
2052             # ---- Casio ----
2053             QVMI => { #PH
2054             Name => 'CasioQVMI',
2055             # Casio stores standard EXIF-format information in MOV videos (eg. EX-S880)
2056             SubDirectory => {
2057             TagTable => 'Image::ExifTool::Exif::Main',
2058             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2059             DirName => 'IFD0',
2060             Multi => 0, # (no NextIFD pointer)
2061             Start => 10,
2062             ByteOrder => 'BigEndian',
2063             },
2064             },
2065             # ---- FujiFilm ----
2066             FFMV => { #PH (FinePix HS20EXR)
2067             Name => 'FujiFilmFFMV',
2068             SubDirectory => { TagTable => 'Image::ExifTool::FujiFilm::FFMV' },
2069             },
2070             MVTG => { #PH (FinePix HS20EXR)
2071             Name => 'FujiFilmMVTG',
2072             SubDirectory => {
2073             TagTable => 'Image::ExifTool::Exif::Main',
2074             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2075             DirName => 'IFD0',
2076             Start => 16,
2077             Base => '$start',
2078             ByteOrder => 'LittleEndian',
2079             },
2080             },
2081             # ---- Garmin ---- (ref PH)
2082             uuid => [{
2083             Name => 'GarminSoftware', # (NC)
2084             Condition => '$$valPt =~ /^VIRBactioncamera/',
2085             RawConv => 'substr($val, 16)',
2086             RawConvInv => '"VIRBactioncamera$val"',
2087             },{
2088             Name => 'GarminModel', # (NC)
2089             Condition => '$$valPt =~ /^\xf7\x6c\xd7\x6a\x07\x5b\x4a\x1e\xb3\x1c\x0e\x7f\xab\x7e\x09\xd4/',
2090             Writable => 0,
2091             RawConv => q{
2092             return undef unless length($val) > 25;
2093             my $len = unpack('x24C', $val);
2094             return undef unless length($val) >= 25 + $len;
2095             return substr($val, 25, $len);
2096             },
2097             },{
2098             # have seen "28 f3 11 e2 b7 91 4f 6f 94 e2 4f 5d ea cb 3c 01" for RicohThetaZ1 accelerometer RADT data (not yet decoded)
2099             # also seen in Garmin MP4:
2100             # 51 0b 63 46 6c fd 4a 17 87 42 ea c9 ea ae b3 bd - seems to contain a duplicate of the trak atom
2101             # b3 e8 21 f4 fe 33 4e 10 8f 92 f5 e1 d4 36 c9 8a - 8 bytes of zeros
2102             Name => 'UUID-Unknown',
2103             Writable => 0,
2104             %unknownInfo,
2105             }],
2106             pmcc => {
2107             Name => 'GarminSettings',
2108             ValueConv => 'substr($val, 4)',
2109             ValueConvInv => '"\0\0\0\x01$val"',
2110             },
2111             # hmtp - 412 bytes: "\0\0\0\x01" then maybe "\0\0\0\x64" and the rest zeros
2112             # vrin - 12 bytes: "\0\0\0\x01" followed by 8 bytes of zero
2113             # ---- GoPro ---- (ref PH)
2114             GoPr => 'GoProType', # (Hero3+)
2115             FIRM => { Name => 'FirmwareVersion', Avoid => 1 }, # (Hero4)
2116             LENS => 'LensSerialNumber', # (Hero4)
2117             CAME => { # (Hero4)
2118             Name => 'SerialNumberHash',
2119             Description => 'Camera Serial Number Hash',
2120             ValueConv => 'unpack("H*",$val)',
2121             ValueConvInv => 'pack("H*",$val)',
2122             },
2123             # SETT? 12 bytes (Hero4)
2124             MUID => { Name => 'MediaUID', ValueConv => 'unpack("H*", $val)' },
2125             # HMMT? 404 bytes (Hero4, all zero)
2126             # BCID? 26 bytes (Hero5, all zero), 36 bytes GoPro Max
2127             # GUMI? 16 bytes (Hero5)
2128             "FOV\0" => 'FieldOfView', #forum8938 (Hero2) seen: "Wide"
2129             GPMF => {
2130             Name => 'GoProGPMF',
2131             SubDirectory => { TagTable => 'Image::ExifTool::GoPro::GPMF' },
2132             },
2133             # free (all zero)
2134             "\xa9TSC" => 'StartTimeScale', # (Hero6)
2135             "\xa9TSZ" => 'StartTimeSampleSize', # (Hero6)
2136             "\xa9TIM" => 'StartTimecode', #PH (NC)
2137             # ---- DJI ----
2138             # \xa9 tags written by DJI Phantom 3: (ref PH)
2139             "\xa9xsp" => 'SpeedX', #PH (guess)
2140             "\xa9ysp" => 'SpeedY', #PH (guess)
2141             "\xa9zsp" => 'SpeedZ', #PH (guess)
2142             "\xa9fpt" => 'Pitch', #PH
2143             "\xa9fyw" => 'Yaw', #PH
2144             "\xa9frl" => 'Roll', #PH
2145             "\xa9gpt" => 'CameraPitch', #PH
2146             "\xa9gyw" => 'CameraYaw', #PH
2147             "\xa9grl" => 'CameraRoll', #PH
2148             "\xa9enc" => 'EncoderID', #PH (forum9271)
2149             # and the following entries don't have the proper 4-byte header for \xa9 tags:
2150             "\xa9dji" => { Name => 'UserData_dji', Format => 'undef', Binary => 1, Unknown => 1, Hidden => 1 },
2151             "\xa9res" => { Name => 'UserData_res', Format => 'undef', Binary => 1, Unknown => 1, Hidden => 1 },
2152             "\xa9uid" => { Name => 'UserData_uid', Format => 'undef', Binary => 1, Unknown => 1, Hidden => 1 },
2153             "\xa9mdl" => {
2154             Name => 'Model',
2155             Notes => 'non-standard-format DJI tag',
2156             Format => 'string',
2157             Avoid => 1,
2158             },
2159             btec => {
2160             Name => 'GlamourSettings',
2161             SubDirectory => { TagTable => 'Image::ExifTool::DJI::Glamour' },
2162             },
2163             fsid => 'OriginalFilePath',
2164             # dbcm - seen "\0\0\0\x04"
2165             # ---- HTC ----
2166             htcb => {
2167             Name => 'HTCBinary',
2168             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HTCBinary' },
2169             },
2170             # ---- Kodak ----
2171             DcMD => {
2172             Name => 'KodakDcMD',
2173             SubDirectory => { TagTable => 'Image::ExifTool::Kodak::DcMD' },
2174             },
2175             SNum => { Name => 'SerialNumber', Avoid => 1, Groups => { 2 => 'Camera' } },
2176             ptch => { Name => 'Pitch', Format => 'rational64s', Avoid => 1 }, # Units??
2177             _yaw => { Name => 'Yaw', Format => 'rational64s', Avoid => 1 }, # Units??
2178             roll => { Name => 'Roll', Format => 'rational64s', Avoid => 1 }, # Units??
2179             _cx_ => { Name => 'CX', Format => 'rational64s', Unknown => 1 },
2180             _cy_ => { Name => 'CY', Format => 'rational64s', Unknown => 1 },
2181             rads => { Name => 'Rads', Format => 'rational64s', Unknown => 1 },
2182             lvlm => { Name => 'LevelMeter', Format => 'rational64s', Unknown => 1 }, # (guess, Kodak proprietary)
2183             Lvlm => { Name => 'LevelMeter', Format => 'rational64s', Unknown => 1 }, # (guess, Kodak proprietary)
2184             pose => { Name => 'pose', SubDirectory => { TagTable => 'Image::ExifTool::Kodak::pose' } },
2185             # AMBA => Ambarella AVC atom (unknown data written by Kodak Playsport video cam)
2186             # tmlp - 1 byte: 0 (PixPro SP360/4KVR360)
2187             # pivi - 72 bytes (PixPro SP360)
2188             # pive - 12 bytes (PixPro SP360)
2189             # loop - 4 bytes: 0 0 0 0 (PixPro 4KVR360)
2190             # m cm - 2 bytes: 0 0 (PixPro 4KVR360)
2191             # m ev - 2 bytes: 0 0 (PixPro SP360/4KVR360) (exposure comp?)
2192             # m vr - 2 bytes: 0 1 (PixPro 4KVR360) (virtual reality?)
2193             # m wb - 4 bytes: 0 0 0 0 (PixPro SP360/4KVR360) (white balance?)
2194             # mclr - 4 bytes: 0 0 0 0 (PixPro SP360/4KVR360)
2195             # mmtr - 4 bytes: 0,6 0 0 0 (PixPro SP360/4KVR360)
2196             # mflr - 4 bytes: 0 0 0 0 (PixPro SP360)
2197             # lvlm - 24 bytes (PixPro SP360)
2198             # Lvlm - 24 bytes (PixPro 4KVR360)
2199             # ufdm - 4 bytes: 0 0 0 1 (PixPro SP360)
2200             # mtdt - 1 byte: 0 (PixPro SP360/4KVR360)
2201             # gdta - 75240 bytes (PixPro SP360)
2202             # EIS1 - 4 bytes: 03 07 00 00 (PixPro 4KVR360)
2203             # EIS2 - 4 bytes: 04 97 00 00 (PixPro 4KVR360)
2204             # ---- LG ----
2205             adzc => { Name => 'Unknown_adzc', Unknown => 1, Hidden => 1, %langText }, # "false\0/","true\0/"
2206             adze => { Name => 'Unknown_adze', Unknown => 1, Hidden => 1, %langText }, # "false\0/"
2207             adzm => { Name => 'Unknown_adzm', Unknown => 1, Hidden => 1, %langText }, # "\x0e\x04/","\x10\x06"
2208             # ---- Microsoft ----
2209             Xtra => { #PH (microsoft)
2210             Name => 'MicrosoftXtra',
2211             WriteGroup => 'Microsoft',
2212             SubDirectory => {
2213             DirName => 'Microsoft',
2214             TagTable => 'Image::ExifTool::Microsoft::Xtra',
2215             },
2216             },
2217             # ---- Minolta ----
2218             MMA0 => { #PH (DiMage 7Hi)
2219             Name => 'MinoltaMMA0',
2220             SubDirectory => { TagTable => 'Image::ExifTool::Minolta::MMA' },
2221             },
2222             MMA1 => { #PH (Dimage A2)
2223             Name => 'MinoltaMMA1',
2224             SubDirectory => { TagTable => 'Image::ExifTool::Minolta::MMA' },
2225             },
2226             # ---- Nikon ----
2227             NCDT => { #PH
2228             Name => 'NikonNCDT',
2229             SubDirectory => { TagTable => 'Image::ExifTool::Nikon::NCDT' },
2230             },
2231             # ---- Olympus ----
2232             scrn => { #PH (TG-810)
2233             Name => 'OlympusPreview',
2234             Condition => '$$valPt =~ /^.{4}\xff\xd8\xff\xdb/s',
2235             SubDirectory => { TagTable => 'Image::ExifTool::Olympus::scrn' },
2236             },
2237             # ---- Panasonic/Leica ----
2238             PANA => { #PH
2239             Name => 'PanasonicPANA',
2240             SubDirectory => { TagTable => 'Image::ExifTool::Panasonic::PANA' },
2241             },
2242             LEIC => { #PH
2243             Name => 'LeicaLEIC',
2244             SubDirectory => { TagTable => 'Image::ExifTool::Panasonic::PANA' },
2245             },
2246             # ---- Pentax ----
2247             thmb => [ # (apparently defined by 3gpp, ref 16)
2248             { #PH (Pentax Q)
2249             Name => 'MakerNotePentax5a',
2250             Condition => '$$valPt =~ /^PENTAX \0II/',
2251             SubDirectory => {
2252             TagTable => 'Image::ExifTool::Pentax::Main',
2253             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2254             Start => 10,
2255             Base => '$start - 10',
2256             ByteOrder => 'LittleEndian',
2257             },
2258             },{ #PH (TG-810)
2259             Name => 'OlympusThumbnail',
2260             Condition => '$$valPt =~ /^.{4}\xff\xd8\xff\xdb/s',
2261             SubDirectory => { TagTable => 'Image::ExifTool::Olympus::thmb' },
2262             },{ #17 (format is in bytes 3-7)
2263             Name => 'ThumbnailImage',
2264             Condition => '$$valPt =~ /^.{8}\xff\xd8\xff[\xdb\xe0]/s',
2265             Groups => { 2 => 'Preview' },
2266             RawConv => 'substr($val, 8)',
2267             Binary => 1,
2268             },{ #17 (format is in bytes 3-7)
2269             Name => 'ThumbnailPNG',
2270             Condition => '$$valPt =~ /^.{8}\x89PNG\r\n\x1a\n/s',
2271             Groups => { 2 => 'Preview' },
2272             RawConv => 'substr($val, 8)',
2273             Binary => 1,
2274             },{
2275             Name => 'UnknownThumbnail',
2276             Groups => { 2 => 'Preview' },
2277             Binary => 1,
2278             },
2279             ],
2280             PENT => { #PH
2281             Name => 'PentaxPENT',
2282             SubDirectory => {
2283             TagTable => 'Image::ExifTool::Pentax::PENT',
2284             ByteOrder => 'LittleEndian',
2285             },
2286             },
2287             PXTH => { #PH (Pentax K-01)
2288             Name => 'PentaxPreview',
2289             SubDirectory => { TagTable => 'Image::ExifTool::Pentax::PXTH' },
2290             },
2291             PXMN => [{ #PH (Pentax K-01)
2292             Name => 'MakerNotePentax5b',
2293             Condition => '$$valPt =~ /^PENTAX \0MM/',
2294             SubDirectory => {
2295             TagTable => 'Image::ExifTool::Pentax::Main',
2296             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2297             Start => 10,
2298             Base => '$start - 10',
2299             ByteOrder => 'BigEndian',
2300             },
2301             },{ #PH (Pentax 645Z)
2302             Name => 'MakerNotePentax5c',
2303             Condition => '$$valPt =~ /^PENTAX \0II/',
2304             SubDirectory => {
2305             TagTable => 'Image::ExifTool::Pentax::Main',
2306             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2307             Start => 10,
2308             Base => '$start - 10',
2309             ByteOrder => 'LittleEndian',
2310             },
2311             },{
2312             Name => 'MakerNoteRicohPentax2',
2313             # used by cameras such as the Ricoh GR III
2314             Condition => '$$valPt=~/^RICOH\0II/',
2315             SubDirectory => {
2316             TagTable => 'Image::ExifTool::Pentax::Main',
2317             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2318             Start => 8,
2319             Base => '$start - 8',
2320             ByteOrder => 'LittleEndian',
2321             },
2322             },{
2323             Name => 'MakerNoteRicohPentax3',
2324             Condition => '$$valPt=~/^RICOH\0MM/', # (just in case)
2325             SubDirectory => {
2326             TagTable => 'Image::ExifTool::Pentax::Main',
2327             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2328             Start => 8,
2329             Base => '$start - 8',
2330             ByteOrder => 'BigEndian',
2331             },
2332             },{
2333             Name => 'MakerNotePentaxUnknown',
2334             Binary => 1,
2335             }],
2336             # ---- Ricoh ----
2337             RICO => { #PH (G900SE)
2338             Name => 'RicohInfo',
2339             Condition => '$$valPt =~ /^\xff\xe1..Exif\0\0/s',
2340             SubDirectory => {
2341             TagTable => 'Image::ExifTool::JPEG::Main',
2342             ProcessProc => \&Image::ExifTool::ProcessJPEG,
2343             }
2344             },
2345             RTHU => { #PH (GR)
2346             Name => 'PreviewImage',
2347             Groups => { 2 => 'Preview' },
2348             RawConv => '$self->ValidateImage(\$val, $tag)',
2349             },
2350             RMKN => { #PH (GR)
2351             Name => 'RicohRMKN',
2352             SubDirectory => {
2353             TagTable => 'Image::ExifTool::Exif::Main',
2354             ProcessProc => \&Image::ExifTool::ProcessTIFF, # (because ProcessMOV is default)
2355             },
2356             },
2357             '@mak' => { Name => 'Make', Avoid => 1 },
2358             '@mod' => { Name => 'Model', Avoid => 1 },
2359             '@swr' => { Name => 'SoftwareVersion', Avoid => 1 },
2360             '@day' => {
2361             Name => 'ContentCreateDate',
2362             Notes => q{
2363             some stupid Ricoh programmer used the '@' symbol instead of the copyright
2364             symbol in these tag ID's for the Ricoh Theta Z1 and maybe other models
2365             },
2366             Groups => { 2 => 'Time' },
2367             Avoid => 1,
2368             # handle values in the form "2010-02-12T13:27:14-0800"
2369             %iso8601Date,
2370             },
2371             '@xyz' => { #PH (iPhone 3GS)
2372             Name => 'GPSCoordinates',
2373             Groups => { 2 => 'Location' },
2374             Avoid => 1,
2375             ValueConv => \&ConvertISO6709,
2376             ValueConvInv => \&ConvInvISO6709,
2377             PrintConv => \&PrintGPSCoordinates,
2378             PrintConvInv => \&PrintInvGPSCoordinates,
2379             },
2380             # RDT1 - pairs of int32u_BE, starting at byte 8: "458275 471846"
2381             # RDT2 - pairs of int32u_BE, starting at byte 8: "472276 468526"
2382             # RDT3 - pairs of int32u_BE, starting at byte 8: "876603 482191"
2383             # RDT4 - pairs of int32u_BE, starting at byte 8: "1955 484612"
2384             # RDT6 - empty
2385             # RDT7 - empty
2386             # RDT8 - empty
2387             # RDT9 - only 16-byte header?
2388             # the boxes below all have a similar header (little-endian):
2389             # 0 int32u - number of records
2390             # 4 int32u - sample rate (Hz)
2391             # 6 int16u - record length in bytes
2392             # 8 int16u - 0x0123 = little-endian, 0x3210 = big endian
2393             # 10 int16u[3] - all zeros
2394             # 16 - start of records (each record ends in an int64u timestamp "ts" in ns)
2395             RDTA => {
2396             Name => 'RicohRDTA',
2397             SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::RDTA' },
2398             },
2399             RDTB => {
2400             Name => 'RicohRDTB',
2401             SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::RDTB' },
2402             },
2403             RDTC => {
2404             Name => 'RicohRDTC',
2405             SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::RDTC' },
2406             },
2407             # RDTD - int16s[3],ts: "353 -914 16354 0 775.829"
2408             RDTG => {
2409             Name => 'RicohRDTG',
2410             SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::RDTG' },
2411             },
2412             # RDTI - float[4],ts: "0.00165951 0.005770059 0.06838259 0.1744695 775.862"
2413             RDTL => {
2414             Name => 'RicohRDTL',
2415             SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::RDTL' },
2416             },
2417             # ---- Samsung ----
2418             vndr => 'Vendor', #PH (Samsung PL70)
2419             SDLN => 'PlayMode', #PH (NC, Samsung ST80 "SEQ_PLAY")
2420             INFO => {
2421             Name => 'SamsungINFO',
2422             SubDirectory => { TagTable => 'Image::ExifTool::Samsung::INFO' },
2423             },
2424             '@sec' => { #PH (Samsung WB30F)
2425             Name => 'SamsungSec',
2426             SubDirectory => { TagTable => 'Image::ExifTool::Samsung::sec' },
2427             },
2428             'smta' => { #PH (Samsung SM-C101)
2429             Name => 'SamsungSmta',
2430             SubDirectory => {
2431             TagTable => 'Image::ExifTool::Samsung::smta',
2432             Start => 4,
2433             },
2434             },
2435             cver => 'CodeVersion', #PH (guess, Samsung MV900F)
2436             # ducp - 4 bytes all zero (Samsung ST96,WB750), 52 bytes all zero (Samsung WB30F)
2437             # edli - 52 bytes all zero (Samsung WB30F)
2438             # @etc - 4 bytes all zero (Samsung WB30F)
2439             # saut - 4 bytes all zero (Samsung SM-N900T)
2440             # smrd - string "TRUEBLUE" (Samsung SM-C101, etc)
2441             # ---- Sigma ----
2442             SIGM => [{
2443             Name => 'SigmaEXIF',
2444             Condition => '$$valPt =~ /^(II\x2a\0|MM\0\x2a)/',
2445             SubDirectory => {
2446             TagTable => 'Image::ExifTool::Exif::Main',
2447             ProcessProc => \&Image::ExifTool::ProcessTIFF, # (because ProcessMOV is default)
2448             },
2449             },{
2450             Name => 'PreviewImage',
2451             # 32-byte header followed by preview image. Length at offset 6 in header
2452             Condition => 'length($$valPt) > 0x20 and length($$valPt) == unpack("x6V",$$valPt) + 0x20',
2453             Groups => { 2 => 'Preview' },
2454             SetBase => 1, # so $$self{BASE} will be set for correct offsets in verbose/html dumps
2455             RawConv => q{
2456             $val = substr($val, 0x20);
2457             my $pt = $self->ValidateImage(\$val, $tag);
2458             if ($pt) {
2459             $$self{BASE} += 0x20;
2460             $$self{DOC_NUM} = ++$$self{DOC_COUNT};
2461             $self->ExtractInfo($pt, { ReEntry => 1 });
2462             $$self{DOC_NUM} = 0;
2463             }
2464             return $pt;
2465             },
2466             }],
2467             # ---- TomTom Bandit Action Cam ----
2468             TTMD => {
2469             Name => 'TomTomMetaData',
2470             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TomTom' },
2471             },
2472             # ---- Samsung Gear 360 ----
2473             vrot => {
2474             Name => 'AccelerometerData',
2475             Notes => q{
2476             accelerometer readings for each frame of the video, expressed as sets of
2477             yaw, pitch and roll angles in degrees
2478             },
2479             Format => 'rational64s',
2480             ValueConv => '$val =~ s/^-?\d+ //; \$val', # (ignore leading version/size words)
2481             },
2482             # m360 - 8 bytes "0 0 0 0 0 0 0 1"
2483             # opax - 164 bytes unknown (center and affine arrays? ref 26)
2484             # opai - 32 bytes (maybe contains a serial number starting at byte 16? - PH) (rgb gains, degamma, gamma? ref 26)
2485             # intv - 16 bytes all zero
2486             # ---- Xiaomi ----
2487             mcvr => {
2488             Name => 'PreviewImage',
2489             Groups => { 2 => 'Preview' },
2490             Binary => 1,
2491             },
2492             # ---- Insta360 ----
2493             nail => {
2494             Name => 'ThumbnailTIFF',
2495             Notes => 'image found in some Insta360 videos, converted to TIFF format',
2496             Groups => { 2 => 'Preview' },
2497             RawConv => q{
2498             return undef if length $val < 8;
2499             my ($w, $h) = unpack('NN', $val);
2500             return undef if length $val < $w * $h + 8;
2501             return MakeTiffHeader($w, $h, 1, 8) . substr($val, 8, $w * $h);
2502             },
2503             Binary => 1,
2504             },
2505             # ---- Nextbase ----
2506             info => 'FirmwareVersion',
2507             'time' => {
2508             Name => 'TimeStamp',
2509             Format => 'int32u', # (followed by 4 unknown bytes 00 0d 00 00)
2510             Writable => 0,
2511             Groups => { 2 => 'Time' },
2512             ValueConv => '$val =~ s/ .*//; ConvertUnixTime($val)',
2513             PrintConv => '$self->ConvertDateTime($val)',
2514             },
2515             infi => {
2516             Name => 'CameraInfo',
2517             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Nextbase' },
2518             },
2519             finm => {
2520             Name => 'OriginalFileName',
2521             Writable => 0,
2522             },
2523             # AMBA ? - (133 bytes)
2524             # nbpl ? - "FP-433-KC"
2525             nbpl => { Name => 'Unknown_nbpl', Unknown => 1, Hidden => 1 },
2526             # maca ? - b8 2d 28 15 f1 48
2527             # sern ? - 0d 69 42 74
2528             # nbid ? - 0d 69 42 74 65 df 72 65 03 de c0 fb 01 01 00 00
2529             # ---- Unknown ----
2530             # CDET - 128 bytes (unknown origin)
2531             # mtyp - 4 bytes all zero (some drone video)
2532             # kgrf - 8 bytes all zero ? (in udta inside trak atom)
2533             # kgcg - 128 bytes 0's and 1's
2534             # kgsi - 4 bytes "00 00 00 80"
2535             # FIEL - 18 bytes "FIEL\0\x01\0\0\0..."
2536             #
2537             # other 3rd-party tags
2538             # (ref http://code.google.com/p/mp4parser/source/browse/trunk/isoparser/src/main/resources/isoparser-default.properties?r=814)
2539             #
2540             ccid => 'ContentID',
2541             icnu => 'IconURI',
2542             infu => 'InfoURL',
2543             cdis => 'ContentDistributorID',
2544             albr => { Name => 'AlbumArtist', Groups => { 2 => 'Author' } },
2545             cvru => 'CoverURI',
2546             lrcu => 'LyricsURI',
2547              
2548             tags => { # found in Audible .m4b audio books (ref PH)
2549             Name => 'Audible_tags',
2550             SubDirectory => { TagTable => 'Image::ExifTool::Audible::tags' },
2551             },
2552             # ludt - directory containing 'tlou' tag
2553             );
2554              
2555             # Unknown information stored in HTC One (M8) videos - PH
2556             %Image::ExifTool::QuickTime::HTCBinary = (
2557             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2558             GROUPS => { 0 => 'MakerNotes', 1 => 'HTC', 2 => 'Video' },
2559             TAG_PREFIX => 'HTCBinary',
2560             FORMAT => 'int32u',
2561             FIRST_ENTRY => 0,
2562             # 0 - values: 1
2563             # 1 - values: 0
2564             # 2 - values: 0
2565             # 3 - values: FileSize minus 12 (why?)
2566             # 4 - values: 12
2567             );
2568              
2569             # TomTom Bandit Action Cam metadata (ref PH)
2570             %Image::ExifTool::QuickTime::TomTom = (
2571             PROCESS_PROC => \&ProcessMOV,
2572             GROUPS => { 2 => 'Video' },
2573             NOTES => 'Tags found in TomTom Bandit Action Cam MP4 videos.',
2574             TTAD => {
2575             Name => 'TomTomAD',
2576             SubDirectory => {
2577             TagTable => 'Image::ExifTool::QuickTime::Stream',
2578             ProcessProc => \&Image::ExifTool::QuickTime::ProcessTTAD,
2579             },
2580             },
2581             TTHL => { Name => 'TomTomHL', Binary => 1, Unknown => 1 }, # (mostly zeros)
2582             # (TTID values are different for each video)
2583             TTID => { Name => 'TomTomID', ValueConv => 'unpack("x4H*",$val)' },
2584             TTVI => { Name => 'TomTomVI', Format => 'int32u', Unknown => 1 }, # seen: "0 1 61 508 508"
2585             # TTVD seen: "normal 720p 60fps 60fps 16/9 wide 1x"
2586             TTVD => { Name => 'TomTomVD', ValueConv => 'my @a = ($val =~ /[\x20-\x7e]+/g); "@a"', List => 1 },
2587             );
2588              
2589             # User-specific media data atoms (ref 11)
2590             %Image::ExifTool::QuickTime::UserMedia = (
2591             PROCESS_PROC => \&ProcessMOV,
2592             GROUPS => { 2 => 'Video' },
2593             MTDT => {
2594             Name => 'MetaData',
2595             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MetaData' },
2596             },
2597             );
2598              
2599             # User-specific media data atoms (ref 11)
2600             %Image::ExifTool::QuickTime::MetaData = (
2601             PROCESS_PROC => \&ProcessMetaData,
2602             GROUPS => { 2 => 'Video' },
2603             TAG_PREFIX => 'MetaData',
2604             0x01 => 'Title',
2605             0x03 => {
2606             Name => 'ProductionDate',
2607             Groups => { 2 => 'Time' },
2608             Shift => 'Time',
2609             Writable => 1,
2610             Permanent => 1,
2611             DelValue => '0000/00/00 00:00:00',
2612             # translate from format "YYYY/mm/dd HH:MM:SS"
2613             ValueConv => '$val=~tr{/}{:}; $val',
2614             ValueConvInv => '$val=~s[^(\d{4}):(\d{2}):][$1/$2/]; $val',
2615             PrintConv => '$self->ConvertDateTime($val)',
2616             PrintConvInv => '$self->InverseDateTime($val)',
2617             },
2618             0x04 => 'Software',
2619             0x05 => 'Product',
2620             0x0a => {
2621             Name => 'TrackProperty',
2622             RawConv => 'my @a=unpack("Nnn",$val); "@a"',
2623             PrintConv => [
2624             { 0 => 'No presentation', BITMASK => { 0 => 'Main track' } },
2625             { 0 => 'No attributes', BITMASK => { 15 => 'Read only' } },
2626             '"Priority $val"',
2627             ],
2628             },
2629             0x0b => {
2630             Name => 'TimeZone',
2631             Groups => { 2 => 'Time' },
2632             Writable => 1,
2633             Permanent => 1,
2634             DelValue => 0,
2635             RawConv => 'Get16s(\$val,0)',
2636             RawConvInv => 'Set16s($val)',
2637             PrintConv => 'TimeZoneString($val)',
2638             PrintConvInv => q{
2639             return undef unless $val =~ /^([-+])(\d{1,2}):?(\d{2})$/'
2640             my $tzmin = $2 * 60 + $3;
2641             $tzmin = -$tzmin if $1 eq '-';
2642             return $tzmin;
2643             }
2644             },
2645             0x0c => {
2646             Name => 'ModifyDate',
2647             Groups => { 2 => 'Time' },
2648             Shift => 'Time',
2649             Writable => 1,
2650             Permanent => 1,
2651             DelValue => '0000/00/00 00:00:00',
2652             # translate from format "YYYY/mm/dd HH:MM:SS"
2653             ValueConv => '$val=~tr{/}{:}; $val',
2654             ValueConvInv => '$val=~s[^(\d{4}):(\d{2}):][$1/$2/]; $val',
2655             PrintConv => '$self->ConvertDateTime($val)',
2656             PrintConvInv => '$self->InverseDateTime($val)',
2657             },
2658             );
2659              
2660             # compressed movie atoms (ref http://wiki.multimedia.cx/index.php?title=QuickTime_container#cmov)
2661             %Image::ExifTool::QuickTime::CMovie = (
2662             PROCESS_PROC => \&ProcessMOV,
2663             GROUPS => { 2 => 'Video' },
2664             dcom => 'Compression',
2665             # cmvd - compressed moov atom data
2666             );
2667              
2668             # Profile atoms (ref 11)
2669             %Image::ExifTool::QuickTime::Profile = (
2670             PROCESS_PROC => \&ProcessMOV,
2671             GROUPS => { 2 => 'Video' },
2672             FPRF => {
2673             Name => 'FileGlobalProfile',
2674             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::FileProf' },
2675             },
2676             APRF => {
2677             Name => 'AudioProfile',
2678             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AudioProf' },
2679             },
2680             VPRF => {
2681             Name => 'VideoProfile',
2682             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::VideoProf' },
2683             },
2684             OLYM => { #PH
2685             Name => 'OlympusOLYM',
2686             SubDirectory => {
2687             TagTable => 'Image::ExifTool::Olympus::OLYM',
2688             ByteOrder => 'BigEndian',
2689             },
2690             },
2691             # PREX - seen written by Sony ILCE-7M5 (apparently contains another video profile?)
2692             );
2693              
2694             # FPRF atom information (ref 11)
2695             %Image::ExifTool::QuickTime::FileProf = (
2696             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2697             GROUPS => { 2 => 'Video' },
2698             FORMAT => 'int32u',
2699             0 => { Name => 'FileProfileVersion', Unknown => 1 }, # unknown = uninteresting
2700             1 => {
2701             Name => 'FileFunctionFlags',
2702             PrintConv => { BITMASK => {
2703             28 => 'Fragmented',
2704             29 => 'Additional tracks',
2705             30 => 'Edited', # (main AV track is edited)
2706             }},
2707             },
2708             # 2 - reserved
2709             );
2710              
2711             # APRF atom information (ref 11)
2712             %Image::ExifTool::QuickTime::AudioProf = (
2713             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2714             GROUPS => { 2 => 'Audio' },
2715             FORMAT => 'int32u',
2716             0 => { Name => 'AudioProfileVersion', Unknown => 1 },
2717             1 => 'AudioTrackID',
2718             2 => {
2719             Name => 'AudioCodec',
2720             Format => 'undef[4]',
2721             },
2722             3 => {
2723             Name => 'AudioCodecInfo',
2724             Unknown => 1,
2725             PrintConv => 'sprintf("0x%.4x", $val)',
2726             },
2727             4 => {
2728             Name => 'AudioAttributes',
2729             PrintConv => { BITMASK => {
2730             0 => 'Encrypted',
2731             1 => 'Variable bitrate',
2732             2 => 'Dual mono',
2733             }},
2734             },
2735             5 => {
2736             Name => 'AudioAvgBitrate',
2737             ValueConv => '$val * 1000',
2738             PrintConv => 'ConvertBitrate($val)',
2739             },
2740             6 => {
2741             Name => 'AudioMaxBitrate',
2742             ValueConv => '$val * 1000',
2743             PrintConv => 'ConvertBitrate($val)',
2744             },
2745             7 => 'AudioSampleRate',
2746             8 => 'AudioChannels',
2747             );
2748              
2749             # VPRF atom information (ref 11)
2750             %Image::ExifTool::QuickTime::VideoProf = (
2751             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2752             GROUPS => { 2 => 'Video' },
2753             FORMAT => 'int32u',
2754             0 => { Name => 'VideoProfileVersion', Unknown => 1 },
2755             1 => 'VideoTrackID',
2756             2 => {
2757             Name => 'VideoCodec',
2758             Format => 'undef[4]',
2759             },
2760             3 => {
2761             Name => 'VideoCodecInfo',
2762             Unknown => 1,
2763             PrintConv => 'sprintf("0x%.4x", $val)',
2764             },
2765             4 => {
2766             Name => 'VideoAttributes',
2767             PrintConv => { BITMASK => {
2768             0 => 'Encrypted',
2769             1 => 'Variable bitrate',
2770             2 => 'Variable frame rate',
2771             3 => 'Interlaced',
2772             }},
2773             },
2774             5 => {
2775             Name => 'VideoAvgBitrate',
2776             ValueConv => '$val * 1000',
2777             PrintConv => 'ConvertBitrate($val)',
2778             },
2779             6 => {
2780             Name => 'VideoMaxBitrate',
2781             ValueConv => '$val * 1000',
2782             PrintConv => 'ConvertBitrate($val)',
2783             },
2784             7 => {
2785             Name => 'VideoAvgFrameRate',
2786             Format => 'fixed32u',
2787             PrintConv => 'int($val * 1000 + 0.5) / 1000',
2788             },
2789             8 => {
2790             Name => 'VideoMaxFrameRate',
2791             Format => 'fixed32u',
2792             PrintConv => 'int($val * 1000 + 0.5) / 1000',
2793             },
2794             9 => {
2795             Name => 'VideoSize',
2796             Format => 'int16u[2]',
2797             PrintConv => '$val=~tr/ /x/; $val',
2798             },
2799             10 => {
2800             Name => 'PixelAspectRatio',
2801             Format => 'int16u[2]',
2802             PrintConv => '$val=~tr/ /:/; $val',
2803             },
2804             );
2805              
2806             # meta atoms
2807             %Image::ExifTool::QuickTime::Meta = (
2808             PROCESS_PROC => \&ProcessMOV,
2809             WRITE_PROC => \&WriteQuickTime,
2810             GROUPS => { 1 => 'Meta', 2 => 'Video' },
2811             ilst => {
2812             Name => 'ItemList',
2813             SubDirectory => {
2814             TagTable => 'Image::ExifTool::QuickTime::ItemList',
2815             HasData => 1, # process atoms as containers with 'data' elements
2816             },
2817             },
2818             # MP4 tags (ref 5)
2819             hdlr => {
2820             Name => 'Handler',
2821             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Handler' },
2822             },
2823             dinf => {
2824             Name => 'DataInfo', # (don't change this name -- used to recognize directory when writing)
2825             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::DataInfo' },
2826             },
2827             ipmc => {
2828             Name => 'IPMPControl',
2829             Flags => ['Binary','Unknown'],
2830             },
2831             iloc => {
2832             Name => 'ItemLocation',
2833             RawConv => \&ParseItemLocation,
2834             WriteHook => \&ParseItemLocation,
2835             Notes => 'parsed, but not extracted as a tag',
2836             },
2837             ipro => {
2838             Name => 'ItemProtection',
2839             Flags => ['Binary','Unknown'],
2840             },
2841             iinf => [{
2842             Name => 'ItemInformation',
2843             Condition => '$$self{LastItemID} = -1; $$valPt =~ /^\0/', # (check for version 0)
2844             SubDirectory => {
2845             TagTable => 'Image::ExifTool::QuickTime::ItemInfo',
2846             Start => 6, # (4-byte version/flags + 2-byte count)
2847             },
2848             },{
2849             Name => 'ItemInformation',
2850             SubDirectory => {
2851             TagTable => 'Image::ExifTool::QuickTime::ItemInfo',
2852             Start => 8, # (4-byte version/flags + 4-byte count)
2853             },
2854             }],
2855             'xml ' => {
2856             Name => 'XML',
2857             Flags => [ 'Binary', 'Protected' ],
2858             BlockExtract => 1,
2859             SubDirectory => {
2860             TagTable => 'Image::ExifTool::XMP::XML',
2861             IgnoreProp => { NonRealTimeMeta => 1 }, # ignore container for Sony 'nrtm'
2862             },
2863             },
2864             'keys' => [{
2865             Name => 'AudioKeys',
2866             Condition => '$$self{MediaType} eq "soun"',
2867             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AudioKeys' },
2868             },{
2869             Name => 'VideoKeys',
2870             Condition => '$$self{MediaType} eq "vide"',
2871             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::VideoKeys' },
2872             },{
2873             Name => 'Keys',
2874             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Keys' },
2875             }],
2876             bxml => {
2877             Name => 'BinaryXML',
2878             Flags => ['Binary','Unknown'],
2879             },
2880             pitm => [{
2881             Name => 'PrimaryItemReference',
2882             Condition => '$$valPt =~ /^\0/', # (version 0?)
2883             RawConv => '$$self{PrimaryItem} = unpack("x4n",$val)',
2884             WriteHook => sub { my ($val,$et) = @_; $$et{PrimaryItem} = unpack("x4n",$val); },
2885             },{
2886             Name => 'PrimaryItemReference',
2887             RawConv => '$$self{PrimaryItem} = unpack("x4N",$val)',
2888             WriteHook => sub { my ($val,$et) = @_; $$et{PrimaryItem} = unpack("x4N",$val); },
2889             }],
2890             free => { #PH
2891             Name => 'Free',
2892             Flags => ['Binary','Unknown'],
2893             },
2894             iprp => {
2895             Name => 'ItemProperties',
2896             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ItemProp' },
2897             },
2898             iref => {
2899             Name => 'ItemReference',
2900             # the version is needed to parse some of the item references
2901             Condition => '$$self{ItemRefVersion} = ord($$valPt); 1',
2902             SubDirectory => {
2903             TagTable => 'Image::ExifTool::QuickTime::ItemRef',
2904             Start => 4,
2905             },
2906             },
2907             idat => {
2908             Name => 'MetaImageSize', #PH (NC)
2909             Condition => '$$self{FileType} eq "HEIC"',
2910             Format => 'int16u',
2911             # (don't know what the first two numbers are for)
2912             PrintConv => '$val =~ s/^(\d+) (\d+) (\d+) (\d+)/${3}x$4/; $val',
2913             },
2914             uuid => [
2915             { #PH (Canon R5/R6 HIF)
2916             Name => 'MetaVersion', # (NC)
2917             Condition => '$$valPt=~/^\x85\xc0\xb6\x87\x82\x0f\x11\xe0\x81\x11\xf4\xce\x46\x2b\x6a\x48/',
2918             RawConv => 'substr($val, 0x14)',
2919             },
2920             {
2921             Name => 'UUID-Unknown',
2922             %unknownInfo,
2923             },
2924             ],
2925             grpl => {
2926             Name => 'Unknown_grpl',
2927             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::grpl' },
2928             },
2929             # ctry - country list (ref 29)
2930             # lang - language list (ref 29)
2931             );
2932              
2933             # unknown grpl container
2934             %Image::ExifTool::QuickTime::grpl = (
2935             PROCESS_PROC => \&ProcessMOV,
2936             GROUPS => { 2 => 'Video' },
2937             # altr - seen "00 00 00 00 00 00 00 41 00 00 00 02 00 00 00 42 00 00 00 2e"
2938             );
2939              
2940             # additional metadata container (ref ISO14496-12:2015)
2941             %Image::ExifTool::QuickTime::OtherMeta = (
2942             PROCESS_PROC => \&ProcessMOV,
2943             WRITE_PROC => \&WriteQuickTime,
2944             GROUPS => { 2 => 'Video' },
2945             mere => {
2946             Name => 'MetaRelation',
2947             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MetaRelation' },
2948             },
2949             meta => {
2950             Name => 'Meta',
2951             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
2952             },
2953             );
2954              
2955             # metabox relation (ref ISO14496-12:2015)
2956             %Image::ExifTool::QuickTime::MetaRelation = (
2957             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2958             GROUPS => { 2 => 'Video' },
2959             FORMAT => 'int32u',
2960             # 0 => 'MetaRelationVersion',
2961             # 1 => 'FirstMetaboxHandlerType',
2962             # 2 => 'FirstMetaboxHandlerType',
2963             # 3 => { Name => 'MetaboxRelation', Format => 'int8u' },
2964             );
2965              
2966             %Image::ExifTool::QuickTime::ItemProp = (
2967             PROCESS_PROC => \&ProcessMOV,
2968             WRITE_PROC => \&WriteQuickTime,
2969             GROUPS => { 2 => 'Image' },
2970             ipco => {
2971             Name => 'ItemPropertyContainer',
2972             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ItemPropCont' },
2973             },
2974             ipma => {
2975             Name => 'ItemPropertyAssociation',
2976             RawConv => \&ParseItemPropAssoc,
2977             # (comment out because we do this manually _before_ ipco when writing)
2978             # WriteHook => \&ParseItemPropAssoc,
2979             Notes => 'parsed, but not extracted as a tag',
2980             },
2981             );
2982              
2983             %Image::ExifTool::QuickTime::ItemPropCont = (
2984             PROCESS_PROC => \&ProcessMOV,
2985             WRITE_PROC => \&WriteQuickTime,
2986             CHECK_PROC => \&CheckQTValue,
2987             PERMANENT => 1, # (can't be deleted)
2988             GROUPS => { 2 => 'Image' },
2989             VARS => { START_INDEX => 1 }, # show verbose indices starting at 1
2990             colr => [{
2991             Name => 'ICC_Profile',
2992             Condition => '$$valPt =~ /^(prof|rICC)/',
2993             # (don't do this because Apple Preview won't display an HEIC with a 0-length profile)
2994             # Permanent => 0, # (in QuickTime, this writes a zero-length box instead of deleting)
2995             SubDirectory => {
2996             TagTable => 'Image::ExifTool::ICC_Profile::Main',
2997             Start => 4,
2998             },
2999             },{
3000             Name => 'ColorRepresentation',
3001             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ColorRep' },
3002             }],
3003             irot => {
3004             Name => 'Rotation',
3005             Format => 'int8u',
3006             Writable => 'int8u',
3007             Protected => 1,
3008             PrintConv => {
3009             0 => 'Horizontal (normal)',
3010             1 => 'Rotate 270 CW',
3011             2 => 'Rotate 180',
3012             3 => 'Rotate 90 CW',
3013             },
3014             },
3015             imir => { # (applied before rotation)
3016             Name => 'Mirroring',
3017             Format => 'int8u',
3018             # yes, I realize this making this writable is useless without the ability to
3019             # create/delete this box because it is the existence of this box that is
3020             # significant. (The people who wrote the specification succeeded in making
3021             # this as complicated as possible because creating/deleting boxes from the
3022             # item property container is a real pain in the ass!)
3023             Writable => 'int8u',
3024             Protected => 1,
3025             PrintConv => {
3026             0 => 'Vertical',
3027             1 => 'Horizontal',
3028             # (it would have been great if the specification allowed for a "no mirroring" value)
3029             },
3030             },
3031             ispe => {
3032             Name => 'ImageSpatialExtent',
3033             Condition => '$$valPt =~ /^\0{4}/', # (version/flags == 0/0)
3034             RawConv => q{
3035             my @dim = unpack("x4N*", $val);
3036             return undef if @dim < 2;
3037             unless ($$self{DOC_NUM}) {
3038             $self->FoundTag(ImageWidth => $dim[0]);
3039             $self->FoundTag(ImageHeight => $dim[1]);
3040             }
3041             return join ' ', @dim;
3042             },
3043             PrintConv => '$val =~ tr/ /x/; $val',
3044             },
3045             pixi => {
3046             Name => 'ImagePixelDepth',
3047             Condition => '$$valPt =~ /^\0{4}./s', # (version/flags == 0/0 and count)
3048             RawConv => 'join " ", unpack("x5C*", $val)',
3049             },
3050             auxC => {
3051             Name => 'AuxiliaryImageType',
3052             Format => 'undef',
3053             RawConv => '$val = substr($val, 4); $val =~ s/\0.*//s; $val',
3054             },
3055             pasp => {
3056             Name => 'PixelAspectRatio',
3057             Format => 'int32u',
3058             Writable => 'int32u',
3059             Count => 2,
3060             Protected => 1,
3061             },
3062             rloc => {
3063             Name => 'RelativeLocation',
3064             Format => 'int32u',
3065             RawConv => '$val =~ s/^\S+\s+//; $val', # remove version/flags
3066             },
3067             clap => {
3068             Name => 'CleanAperture',
3069             Format => 'rational64s',
3070             Notes => '4 numbers: width, height, left and top',
3071             },
3072             hvcC => {
3073             Name => 'HEVCConfiguration',
3074             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HEVCConfig' },
3075             },
3076             av1C => {
3077             Name => 'AV1Configuration',
3078             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AV1Config' },
3079             },
3080             clli => {
3081             Name => 'ContentLightLevel',
3082             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ContentLightLevel' },
3083             },
3084             # ref https://nokiatech.github.io/heif/technical.html
3085             # cclv - Content Color Volume
3086             # mdcv - Mastering Display Color Volume
3087             # rrtp - Required reference types
3088             # crtt - Creation time information
3089             # mdft - Modification time information
3090             # udes - User description
3091             # altt - Accessibility text
3092             # aebr - Auto exposure information
3093             # wbbr - White balance information
3094             # fobr - Focus information
3095             # afbr - Flash exposure information
3096             # dobr - Depth of field information
3097             # pano - Panorama information
3098             # iscl - Image Scaling
3099             );
3100              
3101             # ref https://aomediacodec.github.io/av1-spec/av1-spec.pdf
3102             # (NOTE: conversions are the same as Image::ExifTool::ICC_Profile::ColorRep tags)
3103             %Image::ExifTool::QuickTime::ColorRep = (
3104             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
3105             GROUPS => { 2 => 'Video' },
3106             FIRST_ENTRY => 0,
3107             0 => { Name => 'ColorProfiles', Format => 'undef[4]' },
3108             4 => {
3109             Name => 'ColorPrimaries',
3110             Format => 'int16u',
3111             PrintConv => {
3112             1 => 'BT.709',
3113             2 => 'Unspecified',
3114             4 => 'BT.470 System M (historical)',
3115             5 => 'BT.470 System B, G (historical)',
3116             6 => 'BT.601',
3117             7 => 'SMPTE 240',
3118             8 => 'Generic film (color filters using illuminant C)',
3119             9 => 'BT.2020, BT.2100',
3120             10 => 'SMPTE 428 (CIE 1931 XYZ)', #forum14766
3121             11 => 'SMPTE RP 431-2',
3122             12 => 'SMPTE EG 432-1',
3123             22 => 'EBU Tech. 3213-E',
3124             },
3125             },
3126             6 => {
3127             Name => 'TransferCharacteristics',
3128             Format => 'int16u',
3129             PrintConv => {
3130             0 => 'For future use (0)',
3131             1 => 'BT.709',
3132             2 => 'Unspecified',
3133             3 => 'For future use (3)',
3134             4 => 'BT.470 System M (historical)', # Gamma 2.2? (ref forum14960)
3135             5 => 'BT.470 System B, G (historical)', # Gamma 2.8? (ref forum14960)
3136             6 => 'BT.601',
3137             7 => 'SMPTE 240 M',
3138             8 => 'Linear',
3139             9 => 'Logarithmic (100 : 1 range)',
3140             10 => 'Logarithmic (100 * Sqrt(10) : 1 range)',
3141             11 => 'IEC 61966-2-4',
3142             12 => 'BT.1361',
3143             13 => 'sRGB or sYCC',
3144             14 => 'BT.2020 10-bit systems',
3145             15 => 'BT.2020 12-bit systems',
3146             16 => 'SMPTE ST 2084, ITU BT.2100 PQ',
3147             17 => 'SMPTE ST 428',
3148             18 => 'BT.2100 HLG, ARIB STD-B67',
3149             },
3150             },
3151             8 => {
3152             Name => 'MatrixCoefficients',
3153             Format => 'int16u',
3154             PrintConv => {
3155             0 => 'Identity matrix',
3156             1 => 'BT.709',
3157             2 => 'Unspecified',
3158             3 => 'For future use (3)',
3159             4 => 'US FCC 73.628',
3160             5 => 'BT.470 System B, G (historical)',
3161             6 => 'BT.601',
3162             7 => 'SMPTE 240 M',
3163             8 => 'YCgCo',
3164             9 => 'BT.2020 non-constant luminance, BT.2100 YCbCr',
3165             10 => 'BT.2020 constant luminance',
3166             11 => 'SMPTE ST 2085 YDzDx',
3167             12 => 'Chromaticity-derived non-constant luminance',
3168             13 => 'Chromaticity-derived constant luminance',
3169             14 => 'BT.2100 ICtCp',
3170             },
3171             },
3172             10 => {
3173             Name => 'VideoFullRangeFlag',
3174             Mask => 0x80,
3175             PrintConv => { 0 => 'Limited', 1 => 'Full' },
3176             },
3177             );
3178              
3179             # HEVC configuration (ref https://github.com/MPEGGroup/isobmff/blob/master/IsoLib/libisomediafile/src/HEVCConfigAtom.c)
3180             %Image::ExifTool::QuickTime::HEVCConfig = (
3181             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
3182             GROUPS => { 2 => 'Video' },
3183             FIRST_ENTRY => 0,
3184             0 => 'HEVCConfigurationVersion',
3185             1 => {
3186             Name => 'GeneralProfileSpace',
3187             Mask => 0xc0,
3188             PrintConv => { 0 => 'Conforming' },
3189             },
3190             1.1 => {
3191             Name => 'GeneralTierFlag',
3192             Mask => 0x20,
3193             PrintConv => {
3194             0 => 'Main Tier',
3195             1 => 'High Tier',
3196             },
3197             },
3198             1.2 => {
3199             Name => 'GeneralProfileIDC',
3200             Mask => 0x1f,
3201             PrintConv => {
3202             0 => 'No Profile',
3203             1 => 'Main',
3204             2 => 'Main 10',
3205             3 => 'Main Still Picture',
3206             4 => 'Format Range Extensions',
3207             5 => 'High Throughput',
3208             6 => 'Multiview Main',
3209             7 => 'Scalable Main',
3210             8 => '3D Main',
3211             9 => 'Screen Content Coding Extensions',
3212             10 => 'Scalable Format Range Extensions',
3213             11 => 'High Throughput Screen Content Coding Extensions',
3214             },
3215             },
3216             2 => {
3217             Name => 'GenProfileCompatibilityFlags',
3218             Format => 'int32u',
3219             PrintConv => { BITMASK => {
3220             31 => 'No Profile', # (bit 0 in stream)
3221             30 => 'Main', # (bit 1 in stream)
3222             29 => 'Main 10', # (bit 2 in stream)
3223             28 => 'Main Still Picture', # (bit 3 in stream)
3224             27 => 'Format Range Extensions',# (...)
3225             26 => 'High Throughput',
3226             25 => 'Multiview Main',
3227             24 => 'Scalable Main',
3228             23 => '3D Main',
3229             22 => 'Screen Content Coding Extensions',
3230             21 => 'Scalable Format Range Extensions',
3231             20 => 'High Throughput Screen Content Coding Extensions',
3232             }},
3233             },
3234             6 => {
3235             Name => 'ConstraintIndicatorFlags',
3236             Format => 'int8u[6]',
3237             },
3238             12 => {
3239             Name => 'GeneralLevelIDC',
3240             PrintConv => 'sprintf("%d (level %.1f)", $val, $val/30)',
3241             },
3242             13 => {
3243             Name => 'MinSpatialSegmentationIDC',
3244             Format => 'int16u',
3245             Mask => 0x0fff,
3246             },
3247             15 => {
3248             Name => 'ParallelismType',
3249             Mask => 0x03,
3250             },
3251             16 => {
3252             Name => 'ChromaFormat',
3253             Mask => 0x03,
3254             PrintConv => {
3255             0 => 'Monochrome',
3256             1 => '4:2:0',
3257             2 => '4:2:2',
3258             3 => '4:4:4',
3259             },
3260             },
3261             17 => {
3262             Name => 'BitDepthLuma',
3263             Mask => 0x07,
3264             ValueConv => '$val + 8',
3265             },
3266             18 => {
3267             Name => 'BitDepthChroma',
3268             Mask => 0x07,
3269             ValueConv => '$val + 8',
3270             },
3271             19 => {
3272             Name => 'AverageFrameRate',
3273             Format => 'int16u',
3274             ValueConv => '$val / 256',
3275             },
3276             21 => {
3277             Name => 'ConstantFrameRate',
3278             Mask => 0xc0,
3279             PrintConv => {
3280             0 => 'Unknown',
3281             1 => 'Constant Frame Rate',
3282             2 => 'Each Temporal Layer is Constant Frame Rate',
3283             },
3284             },
3285             21.1 => {
3286             Name => 'NumTemporalLayers',
3287             Mask => 0x38,
3288             },
3289             21.2 => {
3290             Name => 'TemporalIDNested',
3291             Mask => 0x04,
3292             PrintConv => { 0 => 'No', 1 => 'Yes' },
3293             },
3294             #21.3 => {
3295             # Name => 'NALUnitLengthSize',
3296             # Mask => 0x03,
3297             # ValueConv => '$val + 1',
3298             # PrintConv => { 1 => '8-bit', 2 => '16-bit', 4 => '32-bit' },
3299             #},
3300             #22 => 'NumberOfNALUnitArrays',
3301             # (don't decode the NAL unit arrays)
3302             );
3303              
3304             # HEVC configuration (ref https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox)
3305             %Image::ExifTool::QuickTime::AV1Config = (
3306             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
3307             GROUPS => { 2 => 'Video' },
3308             FIRST_ENTRY => 0,
3309             0 => {
3310             Name => 'AV1ConfigurationVersion',
3311             Mask => 0x7f,
3312             },
3313             1.0 => {
3314             Name => 'SeqProfile',
3315             Mask => 0xe0,
3316             Unknown => 1,
3317             },
3318             1.1 => {
3319             Name => 'SeqLevelIdx0',
3320             Mask => 0x1f,
3321             Unknown => 1,
3322             },
3323             2.0 => {
3324             Name => 'SeqTier0',
3325             Mask => 0x80,
3326             Unknown => 1,
3327             },
3328             2.1 => {
3329             Name => 'HighBitDepth',
3330             Mask => 0x40,
3331             Unknown => 1,
3332             },
3333             2.2 => {
3334             Name => 'TwelveBit',
3335             Mask => 0x20,
3336             Unknown => 1,
3337             },
3338             2.3 => {
3339             Name => 'ChromaFormat', # (Monochrome+SubSamplingX+SubSamplingY)
3340             Notes => 'bits: 0x04 = Monochrome, 0x02 = SubSamplingX, 0x01 = SubSamplingY',
3341             Mask => 0x1c,
3342             PrintConv => {
3343             0x00 => 'YUV 4:4:4',
3344             0x02 => 'YUV 4:2:2',
3345             0x03 => 'YUV 4:2:0',
3346             0x07 => 'Monochrome 4:0:0',
3347             },
3348             },
3349             2.4 => {
3350             Name => 'ChromaSamplePosition',
3351             Mask => 0x03,
3352             PrintConv => {
3353             0 => 'Unknown',
3354             1 => 'Vertical',
3355             2 => 'Colocated',
3356             3 => '(reserved)',
3357             },
3358             },
3359             3 => {
3360             Name => 'InitialDelaySamples',
3361             RawConv => '$val & 0x10 ? undef : ($val & 0x0f) + 1',
3362             Unknown => 1,
3363             },
3364             );
3365              
3366             # ref https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/MPEG4Writer.cpp
3367             %Image::ExifTool::QuickTime::ContentLightLevel = (
3368             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
3369             GROUPS => { 2 => 'Video' },
3370             FIRST_ENTRY => 0,
3371             FORMAT => 'int16u',
3372             0 => 'MaxContentLightLevel',
3373             1 => 'MaxPicAverageLightLevel',
3374             );
3375              
3376             %Image::ExifTool::QuickTime::ItemRef = (
3377             PROCESS_PROC => \&ProcessMOV,
3378             WRITE_PROC => \&WriteQuickTime,
3379             GROUPS => { 2 => 'Image' },
3380             # (Note: ExifTool's ItemRefVersion may be used to test the iref version number)
3381             NOTES => q{
3382             The Item reference entries listed in the table below contain information about
3383             the associations between items in the file. This information is used by
3384             ExifTool, but these entries are not extracted as tags.
3385             },
3386             dimg => {
3387             Name => 'DerivedImageRef',
3388             # also parse these for the ID of the primary 'tmap' item
3389             # (tone-mapped image in HDRGainMap HEIC by iPhone 15 and 16)
3390             RawConv => \&ParseContentDescribes,
3391             WriteHook => \&ParseContentDescribes,
3392             },
3393             thmb => { Name => 'ThumbnailRef', RawConv => 'undef' },
3394             auxl => { Name => 'AuxiliaryImageRef', RawConv => 'undef' },
3395             cdsc => {
3396             Name => 'ContentDescribes',
3397             RawConv => \&ParseContentDescribes,
3398             WriteHook => \&ParseContentDescribes,
3399             },
3400             );
3401              
3402             %Image::ExifTool::QuickTime::ItemInfo = (
3403             PROCESS_PROC => \&ProcessMOV,
3404             WRITE_PROC => \&WriteQuickTime,
3405             GROUPS => { 2 => 'Image' },
3406             # avc1 - AVC image
3407             # hvc1 - HEVC image
3408             # lhv1 - L-HEVC image
3409             # infe - ItemInformationEntry
3410             # infe types: avc1,hvc1,lhv1,Exif,xml1,iovl(overlay image),grid,mime,tmap,hvt1(tile image)
3411             # ('tmap' has something to do with the new gainmap written by iPhone 15 and 16)
3412             infe => {
3413             Name => 'ItemInfoEntry',
3414             RawConv => \&ParseItemInfoEntry,
3415             WriteHook => \&ParseItemInfoEntry,
3416             Notes => 'parsed, but not extracted as a tag',
3417             },
3418             );
3419              
3420             # track reference atoms
3421             %Image::ExifTool::QuickTime::TrackRef = (
3422             PROCESS_PROC => \&ProcessMOV,
3423             GROUPS => { 1 => 'Track#', 2 => 'Video' },
3424             chap => { Name => 'ChapterListTrackID', Format => 'int32u' },
3425             tmcd => { Name => 'TimecodeTrack', Format => 'int32u' },
3426             mpod => { #PH (FLIR MP4)
3427             Name => 'ElementaryStreamTrack',
3428             Format => 'int32u',
3429             ValueConv => '$val =~ s/^1 //; $val', # (why 2 numbers? -- ignore the first if "1")
3430             },
3431             # also: iTunesInfo
3432             cdsc => {
3433             Name => 'ContentDescribes',
3434             Format => 'int32u',
3435             PrintConv => '"Track $val"',
3436             },
3437             clcp => { Name => 'ClosedCaptionTrack', Format => 'int32u' }, #29
3438             fall => { Name => 'AlternateFormatTrack', Format => 'int32u' }, #29
3439             folw => { Name => 'SubtitleTrack', Format => 'int32u' }, #29
3440             forc => { Name => 'ForcedSubtitleTrack', Format => 'int32u' }, #29
3441             scpt => { Name => 'TranscriptTrack', Format => 'int32u' }, #29
3442             ssrc => { Name => 'Non-primarySourceTrack', Format => 'int32u' }, #29
3443             sync => { Name => 'SyncronizedTrack', Format => 'int32u' }, #29
3444             # hint - Original media for hint track (ref 29)
3445             # cdep (Structural Dependency QT tag?)
3446             );
3447              
3448             # track aperture mode dimensions atoms
3449             # (ref https://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap2/qtff2.html)
3450             %Image::ExifTool::QuickTime::TrackAperture = (
3451             PROCESS_PROC => \&ProcessMOV,
3452             GROUPS => { 1 => 'Track#', 2 => 'Video' },
3453             clef => {
3454             Name => 'CleanApertureDimensions',
3455             Format => 'fixed32u',
3456             Count => 3,
3457             ValueConv => '$val =~ s/^.*? //; $val', # remove flags word
3458             PrintConv => '$val =~ tr/ /x/; $val',
3459             },
3460             prof => {
3461             Name => 'ProductionApertureDimensions',
3462             Format => 'fixed32u',
3463             Count => 3,
3464             ValueConv => '$val =~ s/^.*? //; $val',
3465             PrintConv => '$val =~ tr/ /x/; $val',
3466             },
3467             enof => {
3468             Name => 'EncodedPixelsDimensions',
3469             Format => 'fixed32u',
3470             Count => 3,
3471             ValueConv => '$val =~ s/^.*? //; $val',
3472             PrintConv => '$val =~ tr/ /x/; $val',
3473             },
3474             );
3475              
3476             # item list atoms
3477             # -> these atoms are unique, and contain one or more 'data' atoms
3478             %Image::ExifTool::QuickTime::ItemList = (
3479             PROCESS_PROC => \&ProcessMOV,
3480             WRITE_PROC => \&WriteQuickTime,
3481             CHECK_PROC => \&CheckQTValue,
3482             WRITABLE => 1,
3483             PREFERRED => 2, # (preferred over UserData and Keys tags when writing)
3484             FORMAT => 'string',
3485             GROUPS => { 1 => 'ItemList', 2 => 'Audio' },
3486             WRITE_GROUP => 'ItemList',
3487             LANG_INFO => \&GetLangInfo,
3488             NOTES => q{
3489             This is the preferred location for creating new QuickTime tags. Tags in
3490             this table support alternate languages which are accessed by adding a
3491             3-character ISO 639-2 language code and an optional ISO 3166-1 alpha 2
3492             country code to the tag name (eg. "ItemList:Title-fra" or
3493             "ItemList::Title-fra-FR"). When creating a new Meta box to contain the
3494             ItemList directory, by default ExifTool adds an 'mdir' (Metadata) Handler
3495             box because Apple software may ignore ItemList tags otherwise, but the API
3496             L option may be set to 0 to avoid this.
3497             },
3498             # in this table, binary 1 and 2-byte "data"-type tags are interpreted as
3499             # int8u and int16u. Multi-byte binary "data" tags are extracted as binary data.
3500             # (Note that the Preferred property is set to 0 for some tags to prevent them
3501             # from being created when a same-named tag already exists in the table)
3502             "\xa9ART" => 'Artist',
3503             "\xa9alb" => 'Album',
3504             "\xa9aut" => { Name => 'Author', Avoid => 1, Groups => { 2 => 'Author' } }, #forum10091 ('auth' is preferred)
3505             "\xa9cmt" => 'Comment',
3506             "\xa9com" => { Name => 'Composer', Avoid => 1, }, # ("\xa9wrt" is preferred in ItemList)
3507             "\xa9day" => {
3508             Name => 'ContentCreateDate',
3509             Groups => { 2 => 'Time' },
3510             %iso8601Date,
3511             },
3512             "\xa9des" => 'Description', #4
3513             "\xa9enc" => 'EncodedBy', #10
3514             "\xa9gen" => 'Genre',
3515             "\xa9grp" => 'Grouping',
3516             "\xa9lyr" => 'Lyrics',
3517             "\xa9nam" => 'Title',
3518             "\xa9too" => 'Encoder',
3519             "\xa9trk" => 'Track',
3520             "\xa9wrt" => 'Composer',
3521             #
3522             # the following tags written by AtomicParsley 0.9.6
3523             # (ref https://exiftool.org/forum/index.php?topic=11455.0)
3524             #
3525             "\xa9st3" => 'Subtitle',
3526             "\xa9con" => 'Conductor',
3527             "\xa9sol" => 'Soloist',
3528             "\xa9arg" => 'Arranger',
3529             "\xa9ope" => 'OriginalArtist',
3530             "\xa9dir" => 'Director',
3531             "\xa9ard" => 'ArtDirector',
3532             "\xa9sne" => 'SoundEngineer',
3533             "\xa9prd" => 'Producer',
3534             "\xa9xpd" => 'ExecutiveProducer',
3535             sdes => 'StoreDescription',
3536             #
3537             '----' => {
3538             Name => 'iTunesInfo',
3539             Deletable => 1, # (deletable via 'iTunes' group)
3540             SubDirectory => {
3541             TagTable => 'Image::ExifTool::QuickTime::iTunesInfo',
3542             DirName => 'iTunes', # (necessary for group 'iTunes' delete)
3543             },
3544             },
3545             aART => { Name => 'AlbumArtist', Groups => { 2 => 'Author' } },
3546             covr => { Name => 'CoverArt', Groups => { 2 => 'Preview' }, Binary => 1 },
3547             cpil => { #10
3548             Name => 'Compilation',
3549             Format => 'int8u', #27 (ref 23 contradicts what AtomicParsley actually writes, which is int8s)
3550             Writable => 'int8s',
3551             PrintConv => { 0 => 'No', 1 => 'Yes' },
3552             },
3553             disk => {
3554             Name => 'DiskNumber',
3555             Format => 'undef', # (necessary to prevent decoding as string!)
3556             ValueConv => q{
3557             return \$val unless length($val) >= 6;
3558             my @a = unpack 'x2nn', $val;
3559             return $a[1] ? join(' of ', @a) : $a[0];
3560             },
3561             ValueConvInv => q{
3562             my @a = $val =~ /\d+/g;
3563             return undef if @a == 0 or @a > 2;
3564             push @a, 0 if @a == 1;
3565             return pack('n3', 0, @a);
3566             },
3567             },
3568             pgap => { #10
3569             Name => 'PlayGap',
3570             Format => 'int8u', #23
3571             Writable => 'int8s', #27
3572             PrintConv => {
3573             0 => 'Insert Gap',
3574             1 => 'No Gap',
3575             },
3576             },
3577             tmpo => {
3578             Name => 'BeatsPerMinute',
3579             # marked as boolean but really int16u in my sample
3580             # (but written as int16s by iTunes and AtomicParsley, ref forum11506)
3581             Format => 'int16u',
3582             Writable => 'int16s',
3583             },
3584             trkn => {
3585             Name => 'TrackNumber',
3586             Format => 'undef', # (necessary to prevent decoding as string!)
3587             ValueConv => q{
3588             return \$val unless length($val) >= 6;
3589             my @a = unpack 'x2nn', $val;
3590             return $a[1] ? join(' of ', @a) : $a[0];
3591             },
3592             # (see forum11501 for discussion about the format used)
3593             ValueConvInv => q{
3594             my @a = $val =~ /\d+/g;
3595             return undef if @a == 0 or @a > 2;
3596             push @a, 0 if @a == 1;
3597             return pack('n4', 0, @a, 0);
3598             },
3599             },
3600             #
3601             # Note: it is possible that the tags below are not being decoded properly
3602             # because I don't have samples to verify many of these - PH
3603             #
3604             akID => { #10
3605             Name => 'AppleStoreAccountType',
3606             Format => 'int8u', #24
3607             Writable => 'int8s', #27
3608             PrintConv => {
3609             0 => 'iTunes',
3610             1 => 'AOL',
3611             },
3612             },
3613             albm => { Name => 'Album', Avoid => 1 }, #(ffmpeg source)
3614             apID => 'AppleStoreAccount',
3615             atID => {
3616             # (ref 10 called this AlbumTitleID or TVSeries)
3617             Name => 'ArtistID', #28 (or Track ID ref https://gist.github.com/maf654321/2b44c7b15d798f0c52ee)
3618             Format => 'int32u',
3619             Writable => 'int32s', #27
3620             },
3621             auth => { Name => 'Author', Groups => { 2 => 'Author' } },
3622             catg => 'Category', #7
3623             cnID => { #10
3624             Name => 'AppleStoreCatalogID',
3625             Format => 'int32u',
3626             Writable => 'int32s', #27
3627             },
3628             cmID => 'ComposerID', #28 (need sample to get format)
3629             cprt => { Name => 'Copyright', Groups => { 2 => 'Author' } },
3630             dscp => { Name => 'Description', Avoid => 1 },
3631             desc => { Name => 'Description', Avoid => 1 }, #7
3632             gnre => { #10
3633             Name => 'Genre',
3634             Avoid => 1,
3635             # (Note: see https://exiftool.org/forum/index.php?topic=11537.0)
3636             Format => 'undef',
3637             ValueConv => 'unpack("n",$val)',
3638             ValueConvInv => '$val =~ /^\d+$/ ? pack("n",$val) : undef',
3639             PrintConv => q{
3640             return $val unless $val =~ /^\d+$/;
3641             require Image::ExifTool::ID3;
3642             Image::ExifTool::ID3::PrintGenre($val - 1); # note the "- 1"
3643             },
3644             PrintConvInv => q{
3645             return $val if $val =~ /^[0-9]+$/;
3646             require Image::ExifTool::ID3;
3647             my $id = Image::ExifTool::ID3::GetGenreID($val);
3648             return unless defined $id and $id =~ /^\d+$/;
3649             return $id + 1;
3650             },
3651             },
3652             egid => 'EpisodeGlobalUniqueID', #7
3653             geID => { #10
3654             Name => 'GenreID',
3655             Format => 'int32u',
3656             Writable => 'int32s', #27
3657             SeparateTable => 1,
3658             # the following lookup is based on http://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/genres
3659             # (see scripts/parse_genre to parse genre JSON file from above)
3660             PrintConv => { #21/PH
3661             2 => 'Music|Blues',
3662             3 => 'Music|Comedy',
3663             4 => "Music|Children's Music",
3664             5 => 'Music|Classical',
3665             6 => 'Music|Country',
3666             7 => 'Music|Electronic',
3667             8 => 'Music|Holiday',
3668             9 => 'Music|Classical|Opera',
3669             10 => 'Music|Singer/Songwriter',
3670             11 => 'Music|Jazz',
3671             12 => 'Music|Latino',
3672             13 => 'Music|New Age',
3673             14 => 'Music|Pop',
3674             15 => 'Music|R&B/Soul',
3675             16 => 'Music|Soundtrack',
3676             17 => 'Music|Dance',
3677             18 => 'Music|Hip-Hop/Rap',
3678             19 => 'Music|World',
3679             20 => 'Music|Alternative',
3680             21 => 'Music|Rock',
3681             22 => 'Music|Christian & Gospel',
3682             23 => 'Music|Vocal',
3683             24 => 'Music|Reggae',
3684             25 => 'Music|Easy Listening',
3685             26 => 'Podcasts',
3686             27 => 'Music|J-Pop',
3687             28 => 'Music|Enka',
3688             29 => 'Music|Anime',
3689             30 => 'Music|Kayokyoku',
3690             31 => 'Music Videos',
3691             32 => 'TV Shows',
3692             33 => 'Movies',
3693             34 => 'Music',
3694             35 => 'iPod Games',
3695             36 => 'App Store',
3696             37 => 'Tones',
3697             38 => 'Books',
3698             39 => 'Mac App Store',
3699             40 => 'Textbooks',
3700             50 => 'Music|Fitness & Workout',
3701             51 => 'Music|Pop|K-Pop',
3702             52 => 'Music|Karaoke',
3703             53 => 'Music|Instrumental',
3704             74 => 'Audiobooks|News',
3705             75 => 'Audiobooks|Programs & Performances',
3706             500 => 'Fitness Music',
3707             501 => 'Fitness Music|Pop',
3708             502 => 'Fitness Music|Dance',
3709             503 => 'Fitness Music|Hip-Hop',
3710             504 => 'Fitness Music|Rock',
3711             505 => 'Fitness Music|Alt/Indie',
3712             506 => 'Fitness Music|Latino',
3713             507 => 'Fitness Music|Country',
3714             508 => 'Fitness Music|World',
3715             509 => 'Fitness Music|New Age',
3716             510 => 'Fitness Music|Classical',
3717             1001 => 'Music|Alternative|College Rock',
3718             1002 => 'Music|Alternative|Goth Rock',
3719             1003 => 'Music|Alternative|Grunge',
3720             1004 => 'Music|Alternative|Indie Rock',
3721             1005 => 'Music|Alternative|New Wave',
3722             1006 => 'Music|Alternative|Punk',
3723             1007 => 'Music|Blues|Chicago Blues',
3724             1009 => 'Music|Blues|Classic Blues',
3725             1010 => 'Music|Blues|Contemporary Blues',
3726             1011 => 'Music|Blues|Country Blues',
3727             1012 => 'Music|Blues|Delta Blues',
3728             1013 => 'Music|Blues|Electric Blues',
3729             1014 => "Music|Children's Music|Lullabies",
3730             1015 => "Music|Children's Music|Sing-Along",
3731             1016 => "Music|Children's Music|Stories",
3732             1017 => 'Music|Classical|Avant-Garde',
3733             1018 => 'Music|Classical|Baroque Era',
3734             1019 => 'Music|Classical|Chamber Music',
3735             1020 => 'Music|Classical|Chant',
3736             1021 => 'Music|Classical|Choral',
3737             1022 => 'Music|Classical|Classical Crossover',
3738             1023 => 'Music|Classical|Early Music',
3739             1024 => 'Music|Classical|Impressionist',
3740             1025 => 'Music|Classical|Medieval Era',
3741             1026 => 'Music|Classical|Minimalism',
3742             1027 => 'Music|Classical|Modern Era',
3743             1028 => 'Music|Classical|Opera',
3744             1029 => 'Music|Classical|Orchestral',
3745             1030 => 'Music|Classical|Renaissance',
3746             1031 => 'Music|Classical|Romantic Era',
3747             1032 => 'Music|Classical|Wedding Music',
3748             1033 => 'Music|Country|Alternative Country',
3749             1034 => 'Music|Country|Americana',
3750             1035 => 'Music|Country|Bluegrass',
3751             1036 => 'Music|Country|Contemporary Bluegrass',
3752             1037 => 'Music|Country|Contemporary Country',
3753             1038 => 'Music|Country|Country Gospel',
3754             1039 => 'Music|Country|Honky Tonk',
3755             1040 => 'Music|Country|Outlaw Country',
3756             1041 => 'Music|Country|Traditional Bluegrass',
3757             1042 => 'Music|Country|Traditional Country',
3758             1043 => 'Music|Country|Urban Cowboy',
3759             1044 => 'Music|Dance|Breakbeat',
3760             1045 => 'Music|Dance|Exercise',
3761             1046 => 'Music|Dance|Garage',
3762             1047 => 'Music|Dance|Hardcore',
3763             1048 => 'Music|Dance|House',
3764             1049 => "Music|Dance|Jungle/Drum'n'bass",
3765             1050 => 'Music|Dance|Techno',
3766             1051 => 'Music|Dance|Trance',
3767             1052 => 'Music|Jazz|Big Band',
3768             1053 => 'Music|Jazz|Bop',
3769             1054 => 'Music|Easy Listening|Lounge',
3770             1055 => 'Music|Easy Listening|Swing',
3771             1056 => 'Music|Electronic|Ambient',
3772             1057 => 'Music|Electronic|Downtempo',
3773             1058 => 'Music|Electronic|Electronica',
3774             1060 => 'Music|Electronic|IDM/Experimental',
3775             1061 => 'Music|Electronic|Industrial',
3776             1062 => 'Music|Singer/Songwriter|Alternative Folk',
3777             1063 => 'Music|Singer/Songwriter|Contemporary Folk',
3778             1064 => 'Music|Singer/Songwriter|Contemporary Singer/Songwriter',
3779             1065 => 'Music|Singer/Songwriter|Folk-Rock',
3780             1066 => 'Music|Singer/Songwriter|New Acoustic',
3781             1067 => 'Music|Singer/Songwriter|Traditional Folk',
3782             1068 => 'Music|Hip-Hop/Rap|Alternative Rap',
3783             1069 => 'Music|Hip-Hop/Rap|Dirty South',
3784             1070 => 'Music|Hip-Hop/Rap|East Coast Rap',
3785             1071 => 'Music|Hip-Hop/Rap|Gangsta Rap',
3786             1072 => 'Music|Hip-Hop/Rap|Hardcore Rap',
3787             1073 => 'Music|Hip-Hop/Rap|Hip-Hop',
3788             1074 => 'Music|Hip-Hop/Rap|Latin Rap',
3789             1075 => 'Music|Hip-Hop/Rap|Old School Rap',
3790             1076 => 'Music|Hip-Hop/Rap|Rap',
3791             1077 => 'Music|Hip-Hop/Rap|Underground Rap',
3792             1078 => 'Music|Hip-Hop/Rap|West Coast Rap',
3793             1079 => 'Music|Holiday|Chanukah',
3794             1080 => 'Music|Holiday|Christmas',
3795             1081 => "Music|Holiday|Christmas: Children's",
3796             1082 => 'Music|Holiday|Christmas: Classic',
3797             1083 => 'Music|Holiday|Christmas: Classical',
3798             1084 => 'Music|Holiday|Christmas: Jazz',
3799             1085 => 'Music|Holiday|Christmas: Modern',
3800             1086 => 'Music|Holiday|Christmas: Pop',
3801             1087 => 'Music|Holiday|Christmas: R&B',
3802             1088 => 'Music|Holiday|Christmas: Religious',
3803             1089 => 'Music|Holiday|Christmas: Rock',
3804             1090 => 'Music|Holiday|Easter',
3805             1091 => 'Music|Holiday|Halloween',
3806             1092 => 'Music|Holiday|Holiday: Other',
3807             1093 => 'Music|Holiday|Thanksgiving',
3808             1094 => 'Music|Christian & Gospel|CCM',
3809             1095 => 'Music|Christian & Gospel|Christian Metal',
3810             1096 => 'Music|Christian & Gospel|Christian Pop',
3811             1097 => 'Music|Christian & Gospel|Christian Rap',
3812             1098 => 'Music|Christian & Gospel|Christian Rock',
3813             1099 => 'Music|Christian & Gospel|Classic Christian',
3814             1100 => 'Music|Christian & Gospel|Contemporary Gospel',
3815             1101 => 'Music|Christian & Gospel|Gospel',
3816             1103 => 'Music|Christian & Gospel|Praise & Worship',
3817             1104 => 'Music|Christian & Gospel|Southern Gospel',
3818             1105 => 'Music|Christian & Gospel|Traditional Gospel',
3819             1106 => 'Music|Jazz|Avant-Garde Jazz',
3820             1107 => 'Music|Jazz|Contemporary Jazz',
3821             1108 => 'Music|Jazz|Crossover Jazz',
3822             1109 => 'Music|Jazz|Dixieland',
3823             1110 => 'Music|Jazz|Fusion',
3824             1111 => 'Music|Jazz|Latin Jazz',
3825             1112 => 'Music|Jazz|Mainstream Jazz',
3826             1113 => 'Music|Jazz|Ragtime',
3827             1114 => 'Music|Jazz|Smooth Jazz',
3828             1115 => 'Music|Latino|Latin Jazz',
3829             1116 => 'Music|Latino|Contemporary Latin',
3830             1117 => 'Music|Latino|Pop Latino',
3831             1118 => 'Music|Latino|Raices', # (Raíces)
3832             1119 => 'Music|Latino|Urbano latino',
3833             1120 => 'Music|Latino|Baladas y Boleros',
3834             1121 => 'Music|Latino|Rock y Alternativo',
3835             1122 => 'Music|Brazilian',
3836             1123 => 'Music|Latino|Musica Mexicana', # (Música Mexicana)
3837             1124 => 'Music|Latino|Musica tropical', # (Música tropical)
3838             1125 => 'Music|New Age|Environmental',
3839             1126 => 'Music|New Age|Healing',
3840             1127 => 'Music|New Age|Meditation',
3841             1128 => 'Music|New Age|Nature',
3842             1129 => 'Music|New Age|Relaxation',
3843             1130 => 'Music|New Age|Travel',
3844             1131 => 'Music|Pop|Adult Contemporary',
3845             1132 => 'Music|Pop|Britpop',
3846             1133 => 'Music|Pop|Pop/Rock',
3847             1134 => 'Music|Pop|Soft Rock',
3848             1135 => 'Music|Pop|Teen Pop',
3849             1136 => 'Music|R&B/Soul|Contemporary R&B',
3850             1137 => 'Music|R&B/Soul|Disco',
3851             1138 => 'Music|R&B/Soul|Doo Wop',
3852             1139 => 'Music|R&B/Soul|Funk',
3853             1140 => 'Music|R&B/Soul|Motown',
3854             1141 => 'Music|R&B/Soul|Neo-Soul',
3855             1142 => 'Music|R&B/Soul|Quiet Storm',
3856             1143 => 'Music|R&B/Soul|Soul',
3857             1144 => 'Music|Rock|Adult Alternative',
3858             1145 => 'Music|Rock|American Trad Rock',
3859             1146 => 'Music|Rock|Arena Rock',
3860             1147 => 'Music|Rock|Blues-Rock',
3861             1148 => 'Music|Rock|British Invasion',
3862             1149 => 'Music|Rock|Death Metal/Black Metal',
3863             1150 => 'Music|Rock|Glam Rock',
3864             1151 => 'Music|Rock|Hair Metal',
3865             1152 => 'Music|Rock|Hard Rock',
3866             1153 => 'Music|Rock|Metal',
3867             1154 => 'Music|Rock|Jam Bands',
3868             1155 => 'Music|Rock|Prog-Rock/Art Rock',
3869             1156 => 'Music|Rock|Psychedelic',
3870             1157 => 'Music|Rock|Rock & Roll',
3871             1158 => 'Music|Rock|Rockabilly',
3872             1159 => 'Music|Rock|Roots Rock',
3873             1160 => 'Music|Rock|Singer/Songwriter',
3874             1161 => 'Music|Rock|Southern Rock',
3875             1162 => 'Music|Rock|Surf',
3876             1163 => 'Music|Rock|Tex-Mex',
3877             1165 => 'Music|Soundtrack|Foreign Cinema',
3878             1166 => 'Music|Soundtrack|Musicals',
3879             1167 => 'Music|Comedy|Novelty',
3880             1168 => 'Music|Soundtrack|Original Score',
3881             1169 => 'Music|Soundtrack|Soundtrack',
3882             1171 => 'Music|Comedy|Standup Comedy',
3883             1172 => 'Music|Soundtrack|TV Soundtrack',
3884             1173 => 'Music|Vocal|Standards',
3885             1174 => 'Music|Vocal|Traditional Pop',
3886             1175 => 'Music|Jazz|Vocal Jazz',
3887             1176 => 'Music|Vocal|Vocal Pop',
3888             1177 => 'Music|African|Afro-Beat',
3889             1178 => 'Music|African|Afro-Pop',
3890             1179 => 'Music|World|Cajun',
3891             1180 => 'Music|World|Celtic',
3892             1181 => 'Music|World|Celtic Folk',
3893             1182 => 'Music|World|Contemporary Celtic',
3894             1183 => 'Music|Reggae|Modern Dancehall',
3895             1184 => 'Music|World|Drinking Songs',
3896             1185 => 'Music|Indian|Indian Pop',
3897             1186 => 'Music|World|Japanese Pop',
3898             1187 => 'Music|World|Klezmer',
3899             1188 => 'Music|World|Polka',
3900             1189 => 'Music|World|Traditional Celtic',
3901             1190 => 'Music|World|Worldbeat',
3902             1191 => 'Music|World|Zydeco',
3903             1192 => 'Music|Reggae|Roots Reggae',
3904             1193 => 'Music|Reggae|Dub',
3905             1194 => 'Music|Reggae|Ska',
3906             1195 => 'Music|World|Caribbean',
3907             1196 => 'Music|World|South America',
3908             1197 => 'Music|Arabic',
3909             1198 => 'Music|World|North America',
3910             1199 => 'Music|World|Hawaii',
3911             1200 => 'Music|World|Australia',
3912             1201 => 'Music|World|Japan',
3913             1202 => 'Music|World|France',
3914             1203 => 'Music|African',
3915             1204 => 'Music|World|Asia',
3916             1205 => 'Music|World|Europe',
3917             1206 => 'Music|World|South Africa',
3918             1207 => 'Music|Jazz|Hard Bop',
3919             1208 => 'Music|Jazz|Trad Jazz',
3920             1209 => 'Music|Jazz|Cool Jazz',
3921             1210 => 'Music|Blues|Acoustic Blues',
3922             1211 => 'Music|Classical|High Classical',
3923             1220 => 'Music|Brazilian|Axe', # (Axé)
3924             1221 => 'Music|Brazilian|Bossa Nova',
3925             1222 => 'Music|Brazilian|Choro',
3926             1223 => 'Music|Brazilian|Forro', # (Forró)
3927             1224 => 'Music|Brazilian|Frevo',
3928             1225 => 'Music|Brazilian|MPB',
3929             1226 => 'Music|Brazilian|Pagode',
3930             1227 => 'Music|Brazilian|Samba',
3931             1228 => 'Music|Brazilian|Sertanejo',
3932             1229 => 'Music|Brazilian|Baile Funk',
3933             1230 => 'Music|Alternative|Chinese Alt',
3934             1231 => 'Music|Alternative|Korean Indie',
3935             1232 => 'Music|Chinese',
3936             1233 => 'Music|Chinese|Chinese Classical',
3937             1234 => 'Music|Chinese|Chinese Flute',
3938             1235 => 'Music|Chinese|Chinese Opera',
3939             1236 => 'Music|Chinese|Chinese Orchestral',
3940             1237 => 'Music|Chinese|Chinese Regional Folk',
3941             1238 => 'Music|Chinese|Chinese Strings',
3942             1239 => 'Music|Chinese|Taiwanese Folk',
3943             1240 => 'Music|Chinese|Tibetan Native Music',
3944             1241 => 'Music|Hip-Hop/Rap|Chinese Hip-Hop',
3945             1242 => 'Music|Hip-Hop/Rap|Korean Hip-Hop',
3946             1243 => 'Music|Korean',
3947             1244 => 'Music|Korean|Korean Classical',
3948             1245 => 'Music|Korean|Korean Trad Song',
3949             1246 => 'Music|Korean|Korean Trad Instrumental',
3950             1247 => 'Music|Korean|Korean Trad Theater',
3951             1248 => 'Music|Rock|Chinese Rock',
3952             1249 => 'Music|Rock|Korean Rock',
3953             1250 => 'Music|Pop|C-Pop',
3954             1251 => 'Music|Pop|Cantopop/HK-Pop',
3955             1252 => 'Music|Pop|Korean Folk-Pop',
3956             1253 => 'Music|Pop|Mandopop',
3957             1254 => 'Music|Pop|Tai-Pop',
3958             1255 => 'Music|Pop|Malaysian Pop',
3959             1256 => 'Music|Pop|Pinoy Pop',
3960             1257 => 'Music|Pop|Original Pilipino Music',
3961             1258 => 'Music|Pop|Manilla Sound',
3962             1259 => 'Music|Pop|Indo Pop',
3963             1260 => 'Music|Pop|Thai Pop',
3964             1261 => 'Music|Vocal|Trot',
3965             1262 => 'Music|Indian',
3966             1263 => 'Music|Indian|Bollywood',
3967             1264 => 'Music|Indian|Regional Indian|Tamil',
3968             1265 => 'Music|Indian|Regional Indian|Telugu',
3969             1266 => 'Music|Indian|Regional Indian',
3970             1267 => 'Music|Indian|Devotional & Spiritual',
3971             1268 => 'Music|Indian|Sufi',
3972             1269 => 'Music|Indian|Indian Classical',
3973             1270 => 'Music|Russian|Russian Chanson',
3974             1271 => 'Music|World|Dini',
3975             1272 => 'Music|Turkish|Halk',
3976             1273 => 'Music|Turkish|Sanat',
3977             1274 => 'Music|World|Dangdut',
3978             1275 => 'Music|World|Indonesian Religious',
3979             1276 => 'Music|World|Calypso',
3980             1277 => 'Music|World|Soca',
3981             1278 => 'Music|Indian|Ghazals',
3982             1279 => 'Music|Indian|Indian Folk',
3983             1280 => 'Music|Turkish|Arabesque',
3984             1281 => 'Music|African|Afrikaans',
3985             1282 => 'Music|World|Farsi',
3986             1283 => 'Music|World|Israeli',
3987             1284 => 'Music|Arabic|Khaleeji',
3988             1285 => 'Music|Arabic|North African',
3989             1286 => 'Music|Arabic|Arabic Pop',
3990             1287 => 'Music|Arabic|Islamic',
3991             1288 => 'Music|Soundtrack|Sound Effects',
3992             1289 => 'Music|Folk',
3993             1290 => 'Music|Orchestral',
3994             1291 => 'Music|Marching',
3995             1293 => 'Music|Pop|Oldies',
3996             1294 => 'Music|Country|Thai Country',
3997             1295 => 'Music|World|Flamenco',
3998             1296 => 'Music|World|Tango',
3999             1297 => 'Music|World|Fado',
4000             1298 => 'Music|World|Iberia',
4001             1299 => 'Music|Russian',
4002             1300 => 'Music|Turkish',
4003             1301 => 'Podcasts|Arts',
4004             1302 => 'Podcasts|Society & Culture|Personal Journals',
4005             1303 => 'Podcasts|Comedy',
4006             1304 => 'Podcasts|Education',
4007             1305 => 'Podcasts|Kids & Family',
4008             1306 => 'Podcasts|Arts|Food',
4009             1307 => 'Podcasts|Health',
4010             1309 => 'Podcasts|TV & Film',
4011             1310 => 'Podcasts|Music',
4012             1311 => 'Podcasts|News & Politics',
4013             1314 => 'Podcasts|Religion & Spirituality',
4014             1315 => 'Podcasts|Science & Medicine',
4015             1316 => 'Podcasts|Sports & Recreation',
4016             1318 => 'Podcasts|Technology',
4017             1320 => 'Podcasts|Society & Culture|Places & Travel',
4018             1321 => 'Podcasts|Business',
4019             1323 => 'Podcasts|Games & Hobbies',
4020             1324 => 'Podcasts|Society & Culture',
4021             1325 => 'Podcasts|Government & Organizations',
4022             1337 => 'Music Videos|Classical|Piano',
4023             1401 => 'Podcasts|Arts|Literature',
4024             1402 => 'Podcasts|Arts|Design',
4025             1404 => 'Podcasts|Games & Hobbies|Video Games',
4026             1405 => 'Podcasts|Arts|Performing Arts',
4027             1406 => 'Podcasts|Arts|Visual Arts',
4028             1410 => 'Podcasts|Business|Careers',
4029             1412 => 'Podcasts|Business|Investing',
4030             1413 => 'Podcasts|Business|Management & Marketing',
4031             1415 => 'Podcasts|Education|K-12',
4032             1416 => 'Podcasts|Education|Higher Education',
4033             1417 => 'Podcasts|Health|Fitness & Nutrition',
4034             1420 => 'Podcasts|Health|Self-Help',
4035             1421 => 'Podcasts|Health|Sexuality',
4036             1438 => 'Podcasts|Religion & Spirituality|Buddhism',
4037             1439 => 'Podcasts|Religion & Spirituality|Christianity',
4038             1440 => 'Podcasts|Religion & Spirituality|Islam',
4039             1441 => 'Podcasts|Religion & Spirituality|Judaism',
4040             1443 => 'Podcasts|Society & Culture|Philosophy',
4041             1444 => 'Podcasts|Religion & Spirituality|Spirituality',
4042             1446 => 'Podcasts|Technology|Gadgets',
4043             1448 => 'Podcasts|Technology|Tech News',
4044             1450 => 'Podcasts|Technology|Podcasting',
4045             1454 => 'Podcasts|Games & Hobbies|Automotive',
4046             1455 => 'Podcasts|Games & Hobbies|Aviation',
4047             1456 => 'Podcasts|Sports & Recreation|Outdoor',
4048             1459 => 'Podcasts|Arts|Fashion & Beauty',
4049             1460 => 'Podcasts|Games & Hobbies|Hobbies',
4050             1461 => 'Podcasts|Games & Hobbies|Other Games',
4051             1462 => 'Podcasts|Society & Culture|History',
4052             1463 => 'Podcasts|Religion & Spirituality|Hinduism',
4053             1464 => 'Podcasts|Religion & Spirituality|Other',
4054             1465 => 'Podcasts|Sports & Recreation|Professional',
4055             1466 => 'Podcasts|Sports & Recreation|College & High School',
4056             1467 => 'Podcasts|Sports & Recreation|Amateur',
4057             1468 => 'Podcasts|Education|Educational Technology',
4058             1469 => 'Podcasts|Education|Language Courses',
4059             1470 => 'Podcasts|Education|Training',
4060             1471 => 'Podcasts|Business|Business News',
4061             1472 => 'Podcasts|Business|Shopping',
4062             1473 => 'Podcasts|Government & Organizations|National',
4063             1474 => 'Podcasts|Government & Organizations|Regional',
4064             1475 => 'Podcasts|Government & Organizations|Local',
4065             1476 => 'Podcasts|Government & Organizations|Non-Profit',
4066             1477 => 'Podcasts|Science & Medicine|Natural Sciences',
4067             1478 => 'Podcasts|Science & Medicine|Medicine',
4068             1479 => 'Podcasts|Science & Medicine|Social Sciences',
4069             1480 => 'Podcasts|Technology|Software How-To',
4070             1481 => 'Podcasts|Health|Alternative Health',
4071             1482 => 'Podcasts|Arts|Books',
4072             1483 => 'Podcasts|Fiction',
4073             1484 => 'Podcasts|Fiction|Drama',
4074             1485 => 'Podcasts|Fiction|Science Fiction',
4075             1486 => 'Podcasts|Fiction|Comedy Fiction',
4076             1487 => 'Podcasts|History',
4077             1488 => 'Podcasts|True Crime',
4078             1489 => 'Podcasts|News',
4079             1490 => 'Podcasts|News|Business News',
4080             1491 => 'Podcasts|Business|Management',
4081             1492 => 'Podcasts|Business|Marketing',
4082             1493 => 'Podcasts|Business|Entrepreneurship',
4083             1494 => 'Podcasts|Business|Non-Profit',
4084             1495 => 'Podcasts|Comedy|Improv',
4085             1496 => 'Podcasts|Comedy|Comedy Interviews',
4086             1497 => 'Podcasts|Comedy|Stand-Up',
4087             1498 => 'Podcasts|Education|Language Learning',
4088             1499 => 'Podcasts|Education|How To',
4089             1500 => 'Podcasts|Education|Self-Improvement',
4090             1501 => 'Podcasts|Education|Courses',
4091             1502 => 'Podcasts|Leisure',
4092             1503 => 'Podcasts|Leisure|Automotive',
4093             1504 => 'Podcasts|Leisure|Aviation',
4094             1505 => 'Podcasts|Leisure|Hobbies',
4095             1506 => 'Podcasts|Leisure|Crafts',
4096             1507 => 'Podcasts|Leisure|Games',
4097             1508 => 'Podcasts|Leisure|Home & Garden',
4098             1509 => 'Podcasts|Leisure|Video Games',
4099             1510 => 'Podcasts|Leisure|Animation & Manga',
4100             1511 => 'Podcasts|Government',
4101             1512 => 'Podcasts|Health & Fitness',
4102             1513 => 'Podcasts|Health & Fitness|Alternative Health',
4103             1514 => 'Podcasts|Health & Fitness|Fitness',
4104             1515 => 'Podcasts|Health & Fitness|Nutrition',
4105             1516 => 'Podcasts|Health & Fitness|Sexuality',
4106             1517 => 'Podcasts|Health & Fitness|Mental Health',
4107             1518 => 'Podcasts|Health & Fitness|Medicine',
4108             1519 => 'Podcasts|Kids & Family|Education for Kids',
4109             1520 => 'Podcasts|Kids & Family|Stories for Kids',
4110             1521 => 'Podcasts|Kids & Family|Parenting',
4111             1522 => 'Podcasts|Kids & Family|Pets & Animals',
4112             1523 => 'Podcasts|Music|Music Commentary',
4113             1524 => 'Podcasts|Music|Music History',
4114             1525 => 'Podcasts|Music|Music Interviews',
4115             1526 => 'Podcasts|News|Daily News',
4116             1527 => 'Podcasts|News|Politics',
4117             1528 => 'Podcasts|News|Tech News',
4118             1529 => 'Podcasts|News|Sports News',
4119             1530 => 'Podcasts|News|News Commentary',
4120             1531 => 'Podcasts|News|Entertainment News',
4121             1532 => 'Podcasts|Religion & Spirituality|Religion',
4122             1533 => 'Podcasts|Science',
4123             1534 => 'Podcasts|Science|Natural Sciences',
4124             1535 => 'Podcasts|Science|Social Sciences',
4125             1536 => 'Podcasts|Science|Mathematics',
4126             1537 => 'Podcasts|Science|Nature',
4127             1538 => 'Podcasts|Science|Astronomy',
4128             1539 => 'Podcasts|Science|Chemistry',
4129             1540 => 'Podcasts|Science|Earth Sciences',
4130             1541 => 'Podcasts|Science|Life Sciences',
4131             1542 => 'Podcasts|Science|Physics',
4132             1543 => 'Podcasts|Society & Culture|Documentary',
4133             1544 => 'Podcasts|Society & Culture|Relationships',
4134             1545 => 'Podcasts|Sports',
4135             1546 => 'Podcasts|Sports|Soccer',
4136             1547 => 'Podcasts|Sports|Football',
4137             1548 => 'Podcasts|Sports|Basketball',
4138             1549 => 'Podcasts|Sports|Baseball',
4139             1550 => 'Podcasts|Sports|Hockey',
4140             1551 => 'Podcasts|Sports|Running',
4141             1552 => 'Podcasts|Sports|Rugby',
4142             1553 => 'Podcasts|Sports|Golf',
4143             1554 => 'Podcasts|Sports|Cricket',
4144             1555 => 'Podcasts|Sports|Wrestling',
4145             1556 => 'Podcasts|Sports|Tennis',
4146             1557 => 'Podcasts|Sports|Volleyball',
4147             1558 => 'Podcasts|Sports|Swimming',
4148             1559 => 'Podcasts|Sports|Wilderness',
4149             1560 => 'Podcasts|Sports|Fantasy Sports',
4150             1561 => 'Podcasts|TV & Film|TV Reviews',
4151             1562 => 'Podcasts|TV & Film|After Shows',
4152             1563 => 'Podcasts|TV & Film|Film Reviews',
4153             1564 => 'Podcasts|TV & Film|Film History',
4154             1565 => 'Podcasts|TV & Film|Film Interviews',
4155             1602 => 'Music Videos|Blues',
4156             1603 => 'Music Videos|Comedy',
4157             1604 => "Music Videos|Children's Music",
4158             1605 => 'Music Videos|Classical',
4159             1606 => 'Music Videos|Country',
4160             1607 => 'Music Videos|Electronic',
4161             1608 => 'Music Videos|Holiday',
4162             1609 => 'Music Videos|Classical|Opera',
4163             1610 => 'Music Videos|Singer/Songwriter',
4164             1611 => 'Music Videos|Jazz',
4165             1612 => 'Music Videos|Latin',
4166             1613 => 'Music Videos|New Age',
4167             1614 => 'Music Videos|Pop',
4168             1615 => 'Music Videos|R&B/Soul',
4169             1616 => 'Music Videos|Soundtrack',
4170             1617 => 'Music Videos|Dance',
4171             1618 => 'Music Videos|Hip-Hop/Rap',
4172             1619 => 'Music Videos|World',
4173             1620 => 'Music Videos|Alternative',
4174             1621 => 'Music Videos|Rock',
4175             1622 => 'Music Videos|Christian & Gospel',
4176             1623 => 'Music Videos|Vocal',
4177             1624 => 'Music Videos|Reggae',
4178             1625 => 'Music Videos|Easy Listening',
4179             1626 => 'Music Videos|Podcasts',
4180             1627 => 'Music Videos|J-Pop',
4181             1628 => 'Music Videos|Enka',
4182             1629 => 'Music Videos|Anime',
4183             1630 => 'Music Videos|Kayokyoku',
4184             1631 => 'Music Videos|Disney',
4185             1632 => 'Music Videos|French Pop',
4186             1633 => 'Music Videos|German Pop',
4187             1634 => 'Music Videos|German Folk',
4188             1635 => 'Music Videos|Alternative|Chinese Alt',
4189             1636 => 'Music Videos|Alternative|Korean Indie',
4190             1637 => 'Music Videos|Chinese',
4191             1638 => 'Music Videos|Chinese|Chinese Classical',
4192             1639 => 'Music Videos|Chinese|Chinese Flute',
4193             1640 => 'Music Videos|Chinese|Chinese Opera',
4194             1641 => 'Music Videos|Chinese|Chinese Orchestral',
4195             1642 => 'Music Videos|Chinese|Chinese Regional Folk',
4196             1643 => 'Music Videos|Chinese|Chinese Strings',
4197             1644 => 'Music Videos|Chinese|Taiwanese Folk',
4198             1645 => 'Music Videos|Chinese|Tibetan Native Music',
4199             1646 => 'Music Videos|Hip-Hop/Rap|Chinese Hip-Hop',
4200             1647 => 'Music Videos|Hip-Hop/Rap|Korean Hip-Hop',
4201             1648 => 'Music Videos|Korean',
4202             1649 => 'Music Videos|Korean|Korean Classical',
4203             1650 => 'Music Videos|Korean|Korean Trad Song',
4204             1651 => 'Music Videos|Korean|Korean Trad Instrumental',
4205             1652 => 'Music Videos|Korean|Korean Trad Theater',
4206             1653 => 'Music Videos|Rock|Chinese Rock',
4207             1654 => 'Music Videos|Rock|Korean Rock',
4208             1655 => 'Music Videos|Pop|C-Pop',
4209             1656 => 'Music Videos|Pop|Cantopop/HK-Pop',
4210             1657 => 'Music Videos|Pop|Korean Folk-Pop',
4211             1658 => 'Music Videos|Pop|Mandopop',
4212             1659 => 'Music Videos|Pop|Tai-Pop',
4213             1660 => 'Music Videos|Pop|Malaysian Pop',
4214             1661 => 'Music Videos|Pop|Pinoy Pop',
4215             1662 => 'Music Videos|Pop|Original Pilipino Music',
4216             1663 => 'Music Videos|Pop|Manilla Sound',
4217             1664 => 'Music Videos|Pop|Indo Pop',
4218             1665 => 'Music Videos|Pop|Thai Pop',
4219             1666 => 'Music Videos|Vocal|Trot',
4220             1671 => 'Music Videos|Brazilian',
4221             1672 => 'Music Videos|Brazilian|Axe', # (Axé)
4222             1673 => 'Music Videos|Brazilian|Baile Funk',
4223             1674 => 'Music Videos|Brazilian|Bossa Nova',
4224             1675 => 'Music Videos|Brazilian|Choro',
4225             1676 => 'Music Videos|Brazilian|Forro',
4226             1677 => 'Music Videos|Brazilian|Frevo',
4227             1678 => 'Music Videos|Brazilian|MPB',
4228             1679 => 'Music Videos|Brazilian|Pagode',
4229             1680 => 'Music Videos|Brazilian|Samba',
4230             1681 => 'Music Videos|Brazilian|Sertanejo',
4231             1682 => 'Music Videos|Classical|High Classical',
4232             1683 => 'Music Videos|Fitness & Workout',
4233             1684 => 'Music Videos|Instrumental',
4234             1685 => 'Music Videos|Jazz|Big Band',
4235             1686 => 'Music Videos|Pop|K-Pop',
4236             1687 => 'Music Videos|Karaoke',
4237             1688 => 'Music Videos|Rock|Heavy Metal',
4238             1689 => 'Music Videos|Spoken Word',
4239             1690 => 'Music Videos|Indian',
4240             1691 => 'Music Videos|Indian|Bollywood',
4241             1692 => 'Music Videos|Indian|Regional Indian|Tamil',
4242             1693 => 'Music Videos|Indian|Regional Indian|Telugu',
4243             1694 => 'Music Videos|Indian|Regional Indian',
4244             1695 => 'Music Videos|Indian|Devotional & Spiritual',
4245             1696 => 'Music Videos|Indian|Sufi',
4246             1697 => 'Music Videos|Indian|Indian Classical',
4247             1698 => 'Music Videos|Russian|Russian Chanson',
4248             1699 => 'Music Videos|World|Dini',
4249             1700 => 'Music Videos|Turkish|Halk',
4250             1701 => 'Music Videos|Turkish|Sanat',
4251             1702 => 'Music Videos|World|Dangdut',
4252             1703 => 'Music Videos|World|Indonesian Religious',
4253             1704 => 'Music Videos|Indian|Indian Pop',
4254             1705 => 'Music Videos|World|Calypso',
4255             1706 => 'Music Videos|World|Soca',
4256             1707 => 'Music Videos|Indian|Ghazals',
4257             1708 => 'Music Videos|Indian|Indian Folk',
4258             1709 => 'Music Videos|Turkish|Arabesque',
4259             1710 => 'Music Videos|African|Afrikaans',
4260             1711 => 'Music Videos|World|Farsi',
4261             1712 => 'Music Videos|World|Israeli',
4262             1713 => 'Music Videos|Arabic',
4263             1714 => 'Music Videos|Arabic|Khaleeji',
4264             1715 => 'Music Videos|Arabic|North African',
4265             1716 => 'Music Videos|Arabic|Arabic Pop',
4266             1717 => 'Music Videos|Arabic|Islamic',
4267             1718 => 'Music Videos|Soundtrack|Sound Effects',
4268             1719 => 'Music Videos|Folk',
4269             1720 => 'Music Videos|Orchestral',
4270             1721 => 'Music Videos|Marching',
4271             1723 => 'Music Videos|Pop|Oldies',
4272             1724 => 'Music Videos|Country|Thai Country',
4273             1725 => 'Music Videos|World|Flamenco',
4274             1726 => 'Music Videos|World|Tango',
4275             1727 => 'Music Videos|World|Fado',
4276             1728 => 'Music Videos|World|Iberia',
4277             1729 => 'Music Videos|Russian',
4278             1730 => 'Music Videos|Turkish',
4279             1731 => 'Music Videos|Alternative|College Rock',
4280             1732 => 'Music Videos|Alternative|Goth Rock',
4281             1733 => 'Music Videos|Alternative|Grunge',
4282             1734 => 'Music Videos|Alternative|Indie Rock',
4283             1735 => 'Music Videos|Alternative|New Wave',
4284             1736 => 'Music Videos|Alternative|Punk',
4285             1737 => 'Music Videos|Blues|Acoustic Blues',
4286             1738 => 'Music Videos|Blues|Chicago Blues',
4287             1739 => 'Music Videos|Blues|Classic Blues',
4288             1740 => 'Music Videos|Blues|Contemporary Blues',
4289             1741 => 'Music Videos|Blues|Country Blues',
4290             1742 => 'Music Videos|Blues|Delta Blues',
4291             1743 => 'Music Videos|Blues|Electric Blues',
4292             1744 => "Music Videos|Children's Music|Lullabies",
4293             1745 => "Music Videos|Children's Music|Sing-Along",
4294             1746 => "Music Videos|Children's Music|Stories",
4295             1747 => 'Music Videos|Christian & Gospel|CCM',
4296             1748 => 'Music Videos|Christian & Gospel|Christian Metal',
4297             1749 => 'Music Videos|Christian & Gospel|Christian Pop',
4298             1750 => 'Music Videos|Christian & Gospel|Christian Rap',
4299             1751 => 'Music Videos|Christian & Gospel|Christian Rock',
4300             1752 => 'Music Videos|Christian & Gospel|Classic Christian',
4301             1753 => 'Music Videos|Christian & Gospel|Contemporary Gospel',
4302             1754 => 'Music Videos|Christian & Gospel|Gospel',
4303             1755 => 'Music Videos|Christian & Gospel|Praise & Worship',
4304             1756 => 'Music Videos|Christian & Gospel|Southern Gospel',
4305             1757 => 'Music Videos|Christian & Gospel|Traditional Gospel',
4306             1758 => 'Music Videos|Classical|Avant-Garde',
4307             1759 => 'Music Videos|Classical|Baroque Era',
4308             1760 => 'Music Videos|Classical|Chamber Music',
4309             1761 => 'Music Videos|Classical|Chant',
4310             1762 => 'Music Videos|Classical|Choral',
4311             1763 => 'Music Videos|Classical|Classical Crossover',
4312             1764 => 'Music Videos|Classical|Early Music',
4313             1765 => 'Music Videos|Classical|Impressionist',
4314             1766 => 'Music Videos|Classical|Medieval Era',
4315             1767 => 'Music Videos|Classical|Minimalism',
4316             1768 => 'Music Videos|Classical|Modern Era',
4317             1769 => 'Music Videos|Classical|Orchestral',
4318             1770 => 'Music Videos|Classical|Renaissance',
4319             1771 => 'Music Videos|Classical|Romantic Era',
4320             1772 => 'Music Videos|Classical|Wedding Music',
4321             1773 => 'Music Videos|Comedy|Novelty',
4322             1774 => 'Music Videos|Comedy|Standup Comedy',
4323             1775 => 'Music Videos|Country|Alternative Country',
4324             1776 => 'Music Videos|Country|Americana',
4325             1777 => 'Music Videos|Country|Bluegrass',
4326             1778 => 'Music Videos|Country|Contemporary Bluegrass',
4327             1779 => 'Music Videos|Country|Contemporary Country',
4328             1780 => 'Music Videos|Country|Country Gospel',
4329             1781 => 'Music Videos|Country|Honky Tonk',
4330             1782 => 'Music Videos|Country|Outlaw Country',
4331             1783 => 'Music Videos|Country|Traditional Bluegrass',
4332             1784 => 'Music Videos|Country|Traditional Country',
4333             1785 => 'Music Videos|Country|Urban Cowboy',
4334             1786 => 'Music Videos|Dance|Breakbeat',
4335             1787 => 'Music Videos|Dance|Exercise',
4336             1788 => 'Music Videos|Dance|Garage',
4337             1789 => 'Music Videos|Dance|Hardcore',
4338             1790 => 'Music Videos|Dance|House',
4339             1791 => "Music Videos|Dance|Jungle/Drum'n'bass",
4340             1792 => 'Music Videos|Dance|Techno',
4341             1793 => 'Music Videos|Dance|Trance',
4342             1794 => 'Music Videos|Easy Listening|Lounge',
4343             1795 => 'Music Videos|Easy Listening|Swing',
4344             1796 => 'Music Videos|Electronic|Ambient',
4345             1797 => 'Music Videos|Electronic|Downtempo',
4346             1798 => 'Music Videos|Electronic|Electronica',
4347             1799 => 'Music Videos|Electronic|IDM/Experimental',
4348             1800 => 'Music Videos|Electronic|Industrial',
4349             1801 => 'Music Videos|Hip-Hop/Rap|Alternative Rap',
4350             1802 => 'Music Videos|Hip-Hop/Rap|Dirty South',
4351             1803 => 'Music Videos|Hip-Hop/Rap|East Coast Rap',
4352             1804 => 'Music Videos|Hip-Hop/Rap|Gangsta Rap',
4353             1805 => 'Music Videos|Hip-Hop/Rap|Hardcore Rap',
4354             1806 => 'Music Videos|Hip-Hop/Rap|Hip-Hop',
4355             1807 => 'Music Videos|Hip-Hop/Rap|Latin Rap',
4356             1808 => 'Music Videos|Hip-Hop/Rap|Old School Rap',
4357             1809 => 'Music Videos|Hip-Hop/Rap|Rap',
4358             1810 => 'Music Videos|Hip-Hop/Rap|Underground Rap',
4359             1811 => 'Music Videos|Hip-Hop/Rap|West Coast Rap',
4360             1812 => 'Music Videos|Holiday|Chanukah',
4361             1813 => 'Music Videos|Holiday|Christmas',
4362             1814 => "Music Videos|Holiday|Christmas: Children's",
4363             1815 => 'Music Videos|Holiday|Christmas: Classic',
4364             1816 => 'Music Videos|Holiday|Christmas: Classical',
4365             1817 => 'Music Videos|Holiday|Christmas: Jazz',
4366             1818 => 'Music Videos|Holiday|Christmas: Modern',
4367             1819 => 'Music Videos|Holiday|Christmas: Pop',
4368             1820 => 'Music Videos|Holiday|Christmas: R&B',
4369             1821 => 'Music Videos|Holiday|Christmas: Religious',
4370             1822 => 'Music Videos|Holiday|Christmas: Rock',
4371             1823 => 'Music Videos|Holiday|Easter',
4372             1824 => 'Music Videos|Holiday|Halloween',
4373             1825 => 'Music Videos|Holiday|Thanksgiving',
4374             1826 => 'Music Videos|Jazz|Avant-Garde Jazz',
4375             1828 => 'Music Videos|Jazz|Bop',
4376             1829 => 'Music Videos|Jazz|Contemporary Jazz',
4377             1830 => 'Music Videos|Jazz|Cool Jazz',
4378             1831 => 'Music Videos|Jazz|Crossover Jazz',
4379             1832 => 'Music Videos|Jazz|Dixieland',
4380             1833 => 'Music Videos|Jazz|Fusion',
4381             1834 => 'Music Videos|Jazz|Hard Bop',
4382             1835 => 'Music Videos|Jazz|Latin Jazz',
4383             1836 => 'Music Videos|Jazz|Mainstream Jazz',
4384             1837 => 'Music Videos|Jazz|Ragtime',
4385             1838 => 'Music Videos|Jazz|Smooth Jazz',
4386             1839 => 'Music Videos|Jazz|Trad Jazz',
4387             1840 => 'Music Videos|Latin|Alternative & Rock in Spanish',
4388             1841 => 'Music Videos|Latin|Baladas y Boleros',
4389             1842 => 'Music Videos|Latin|Contemporary Latin',
4390             1843 => 'Music Videos|Latin|Latin Jazz',
4391             1844 => 'Music Videos|Latin|Latin Urban',
4392             1845 => 'Music Videos|Latin|Pop in Spanish',
4393             1846 => 'Music Videos|Latin|Raices',
4394             1847 => 'Music Videos|Latin|Musica Mexicana', # (Música Mexicana)
4395             1848 => 'Music Videos|Latin|Salsa y Tropical',
4396             1849 => 'Music Videos|New Age|Healing',
4397             1850 => 'Music Videos|New Age|Meditation',
4398             1851 => 'Music Videos|New Age|Nature',
4399             1852 => 'Music Videos|New Age|Relaxation',
4400             1853 => 'Music Videos|New Age|Travel',
4401             1854 => 'Music Videos|Pop|Adult Contemporary',
4402             1855 => 'Music Videos|Pop|Britpop',
4403             1856 => 'Music Videos|Pop|Pop/Rock',
4404             1857 => 'Music Videos|Pop|Soft Rock',
4405             1858 => 'Music Videos|Pop|Teen Pop',
4406             1859 => 'Music Videos|R&B/Soul|Contemporary R&B',
4407             1860 => 'Music Videos|R&B/Soul|Disco',
4408             1861 => 'Music Videos|R&B/Soul|Doo Wop',
4409             1862 => 'Music Videos|R&B/Soul|Funk',
4410             1863 => 'Music Videos|R&B/Soul|Motown',
4411             1864 => 'Music Videos|R&B/Soul|Neo-Soul',
4412             1865 => 'Music Videos|R&B/Soul|Soul',
4413             1866 => 'Music Videos|Reggae|Modern Dancehall',
4414             1867 => 'Music Videos|Reggae|Dub',
4415             1868 => 'Music Videos|Reggae|Roots Reggae',
4416             1869 => 'Music Videos|Reggae|Ska',
4417             1870 => 'Music Videos|Rock|Adult Alternative',
4418             1871 => 'Music Videos|Rock|American Trad Rock',
4419             1872 => 'Music Videos|Rock|Arena Rock',
4420             1873 => 'Music Videos|Rock|Blues-Rock',
4421             1874 => 'Music Videos|Rock|British Invasion',
4422             1875 => 'Music Videos|Rock|Death Metal/Black Metal',
4423             1876 => 'Music Videos|Rock|Glam Rock',
4424             1877 => 'Music Videos|Rock|Hair Metal',
4425             1878 => 'Music Videos|Rock|Hard Rock',
4426             1879 => 'Music Videos|Rock|Jam Bands',
4427             1880 => 'Music Videos|Rock|Prog-Rock/Art Rock',
4428             1881 => 'Music Videos|Rock|Psychedelic',
4429             1882 => 'Music Videos|Rock|Rock & Roll',
4430             1883 => 'Music Videos|Rock|Rockabilly',
4431             1884 => 'Music Videos|Rock|Roots Rock',
4432             1885 => 'Music Videos|Rock|Singer/Songwriter',
4433             1886 => 'Music Videos|Rock|Southern Rock',
4434             1887 => 'Music Videos|Rock|Surf',
4435             1888 => 'Music Videos|Rock|Tex-Mex',
4436             1889 => 'Music Videos|Singer/Songwriter|Alternative Folk',
4437             1890 => 'Music Videos|Singer/Songwriter|Contemporary Folk',
4438             1891 => 'Music Videos|Singer/Songwriter|Contemporary Singer/Songwriter',
4439             1892 => 'Music Videos|Singer/Songwriter|Folk-Rock',
4440             1893 => 'Music Videos|Singer/Songwriter|New Acoustic',
4441             1894 => 'Music Videos|Singer/Songwriter|Traditional Folk',
4442             1895 => 'Music Videos|Soundtrack|Foreign Cinema',
4443             1896 => 'Music Videos|Soundtrack|Musicals',
4444             1897 => 'Music Videos|Soundtrack|Original Score',
4445             1898 => 'Music Videos|Soundtrack|Soundtrack',
4446             1899 => 'Music Videos|Soundtrack|TV Soundtrack',
4447             1900 => 'Music Videos|Vocal|Standards',
4448             1901 => 'Music Videos|Vocal|Traditional Pop',
4449             1902 => 'Music Videos|Jazz|Vocal Jazz',
4450             1903 => 'Music Videos|Vocal|Vocal Pop',
4451             1904 => 'Music Videos|African',
4452             1905 => 'Music Videos|African|Afro-Beat',
4453             1906 => 'Music Videos|African|Afro-Pop',
4454             1907 => 'Music Videos|World|Asia',
4455             1908 => 'Music Videos|World|Australia',
4456             1909 => 'Music Videos|World|Cajun',
4457             1910 => 'Music Videos|World|Caribbean',
4458             1911 => 'Music Videos|World|Celtic',
4459             1912 => 'Music Videos|World|Celtic Folk',
4460             1913 => 'Music Videos|World|Contemporary Celtic',
4461             1914 => 'Music Videos|World|Europe',
4462             1915 => 'Music Videos|World|France',
4463             1916 => 'Music Videos|World|Hawaii',
4464             1917 => 'Music Videos|World|Japan',
4465             1918 => 'Music Videos|World|Klezmer',
4466             1919 => 'Music Videos|World|North America',
4467             1920 => 'Music Videos|World|Polka',
4468             1921 => 'Music Videos|World|South Africa',
4469             1922 => 'Music Videos|World|South America',
4470             1923 => 'Music Videos|World|Traditional Celtic',
4471             1924 => 'Music Videos|World|Worldbeat',
4472             1925 => 'Music Videos|World|Zydeco',
4473             1926 => 'Music Videos|Christian & Gospel',
4474             1928 => 'Music Videos|Classical|Art Song',
4475             1929 => 'Music Videos|Classical|Brass & Woodwinds',
4476             1930 => 'Music Videos|Classical|Solo Instrumental',
4477             1931 => 'Music Videos|Classical|Contemporary Era',
4478             1932 => 'Music Videos|Classical|Oratorio',
4479             1933 => 'Music Videos|Classical|Cantata',
4480             1934 => 'Music Videos|Classical|Electronic',
4481             1935 => 'Music Videos|Classical|Sacred',
4482             1936 => 'Music Videos|Classical|Guitar',
4483             1938 => 'Music Videos|Classical|Violin',
4484             1939 => 'Music Videos|Classical|Cello',
4485             1940 => 'Music Videos|Classical|Percussion',
4486             1941 => 'Music Videos|Electronic|Dubstep',
4487             1942 => 'Music Videos|Electronic|Bass',
4488             1943 => 'Music Videos|Hip-Hop/Rap|UK Hip-Hop',
4489             1944 => 'Music Videos|Reggae|Lovers Rock',
4490             1945 => 'Music Videos|Alternative|EMO',
4491             1946 => 'Music Videos|Alternative|Pop Punk',
4492             1947 => 'Music Videos|Alternative|Indie Pop',
4493             1948 => 'Music Videos|New Age|Yoga',
4494             1949 => 'Music Videos|Pop|Tribute',
4495             1950 => 'Music Videos|Pop|Shows',
4496             1951 => 'Music Videos|Cuban',
4497             1952 => 'Music Videos|Cuban|Mambo',
4498             1953 => 'Music Videos|Cuban|Chachacha',
4499             1954 => 'Music Videos|Cuban|Guajira',
4500             1955 => 'Music Videos|Cuban|Son',
4501             1956 => 'Music Videos|Cuban|Bolero',
4502             1957 => 'Music Videos|Cuban|Guaracha',
4503             1958 => 'Music Videos|Cuban|Timba',
4504             1959 => 'Music Videos|Soundtrack|Video Game',
4505             1960 => 'Music Videos|Indian|Regional Indian|Punjabi|Punjabi Pop',
4506             1961 => 'Music Videos|Indian|Regional Indian|Bengali|Rabindra Sangeet',
4507             1962 => 'Music Videos|Indian|Regional Indian|Malayalam',
4508             1963 => 'Music Videos|Indian|Regional Indian|Kannada',
4509             1964 => 'Music Videos|Indian|Regional Indian|Marathi',
4510             1965 => 'Music Videos|Indian|Regional Indian|Gujarati',
4511             1966 => 'Music Videos|Indian|Regional Indian|Assamese',
4512             1967 => 'Music Videos|Indian|Regional Indian|Bhojpuri',
4513             1968 => 'Music Videos|Indian|Regional Indian|Haryanvi',
4514             1969 => 'Music Videos|Indian|Regional Indian|Odia',
4515             1970 => 'Music Videos|Indian|Regional Indian|Rajasthani',
4516             1971 => 'Music Videos|Indian|Regional Indian|Urdu',
4517             1972 => 'Music Videos|Indian|Regional Indian|Punjabi',
4518             1973 => 'Music Videos|Indian|Regional Indian|Bengali',
4519             1974 => 'Music Videos|Indian|Indian Classical|Carnatic Classical',
4520             1975 => 'Music Videos|Indian|Indian Classical|Hindustani Classical',
4521             1976 => 'Music Videos|African|Afro House',
4522             1977 => 'Music Videos|African|Afro Soul',
4523             1978 => 'Music Videos|African|Afrobeats',
4524             1979 => 'Music Videos|African|Benga',
4525             1980 => 'Music Videos|African|Bongo-Flava',
4526             1981 => 'Music Videos|African|Coupe-Decale',
4527             1982 => 'Music Videos|African|Gqom',
4528             1983 => 'Music Videos|African|Highlife',
4529             1984 => 'Music Videos|African|Kuduro',
4530             1985 => 'Music Videos|African|Kizomba',
4531             1986 => 'Music Videos|African|Kwaito',
4532             1987 => 'Music Videos|African|Mbalax',
4533             1988 => 'Music Videos|African|Ndombolo',
4534             1989 => 'Music Videos|African|Shangaan Electro',
4535             1990 => 'Music Videos|African|Soukous',
4536             1991 => 'Music Videos|African|Taarab',
4537             1992 => 'Music Videos|African|Zouglou',
4538             1993 => 'Music Videos|Turkish|Ozgun',
4539             1994 => 'Music Videos|Turkish|Fantezi',
4540             1995 => 'Music Videos|Turkish|Religious',
4541             1996 => 'Music Videos|Pop|Turkish Pop',
4542             1997 => 'Music Videos|Rock|Turkish Rock',
4543             1998 => 'Music Videos|Alternative|Turkish Alternative',
4544             1999 => 'Music Videos|Hip-Hop/Rap|Turkish Hip-Hop/Rap',
4545             2000 => 'Music Videos|African|Maskandi',
4546             2001 => 'Music Videos|Russian|Russian Romance',
4547             2002 => 'Music Videos|Russian|Russian Bard',
4548             2003 => 'Music Videos|Russian|Russian Pop',
4549             2004 => 'Music Videos|Russian|Russian Rock',
4550             2005 => 'Music Videos|Russian|Russian Hip-Hop',
4551             2006 => 'Music Videos|Arabic|Levant',
4552             2007 => 'Music Videos|Arabic|Levant|Dabke',
4553             2008 => 'Music Videos|Arabic|Maghreb Rai',
4554             2009 => 'Music Videos|Arabic|Khaleeji|Khaleeji Jalsat',
4555             2010 => 'Music Videos|Arabic|Khaleeji|Khaleeji Shailat',
4556             2011 => 'Music Videos|Tarab',
4557             2012 => 'Music Videos|Tarab|Iraqi Tarab',
4558             2013 => 'Music Videos|Tarab|Egyptian Tarab',
4559             2014 => 'Music Videos|Tarab|Khaleeji Tarab',
4560             2015 => 'Music Videos|Pop|Levant Pop',
4561             2016 => 'Music Videos|Pop|Iraqi Pop',
4562             2017 => 'Music Videos|Pop|Egyptian Pop',
4563             2018 => 'Music Videos|Pop|Maghreb Pop',
4564             2019 => 'Music Videos|Pop|Khaleeji Pop',
4565             2020 => 'Music Videos|Hip-Hop/Rap|Levant Hip-Hop',
4566             2021 => 'Music Videos|Hip-Hop/Rap|Egyptian Hip-Hop',
4567             2022 => 'Music Videos|Hip-Hop/Rap|Maghreb Hip-Hop',
4568             2023 => 'Music Videos|Hip-Hop/Rap|Khaleeji Hip-Hop',
4569             2024 => 'Music Videos|Alternative|Indie Levant',
4570             2025 => 'Music Videos|Alternative|Indie Egyptian',
4571             2026 => 'Music Videos|Alternative|Indie Maghreb',
4572             2027 => 'Music Videos|Electronic|Levant Electronic',
4573             2028 => "Music Videos|Electronic|Electro-Cha'abi",
4574             2029 => 'Music Videos|Electronic|Maghreb Electronic',
4575             2030 => 'Music Videos|Folk|Iraqi Folk',
4576             2031 => 'Music Videos|Folk|Khaleeji Folk',
4577             2032 => 'Music Videos|Dance|Maghreb Dance',
4578             4000 => 'TV Shows|Comedy',
4579             4001 => 'TV Shows|Drama',
4580             4002 => 'TV Shows|Animation',
4581             4003 => 'TV Shows|Action & Adventure',
4582             4004 => 'TV Shows|Classics',
4583             4005 => 'TV Shows|Kids & Family',
4584             4006 => 'TV Shows|Nonfiction',
4585             4007 => 'TV Shows|Reality TV',
4586             4008 => 'TV Shows|Sci-Fi & Fantasy',
4587             4009 => 'TV Shows|Sports',
4588             4010 => 'TV Shows|Teens',
4589             4011 => 'TV Shows|Latino TV',
4590             4401 => 'Movies|Action & Adventure',
4591             4402 => 'Movies|Anime',
4592             4403 => 'Movies|Classics',
4593             4404 => 'Movies|Comedy',
4594             4405 => 'Movies|Documentary',
4595             4406 => 'Movies|Drama',
4596             4407 => 'Movies|Foreign',
4597             4408 => 'Movies|Horror',
4598             4409 => 'Movies|Independent',
4599             4410 => 'Movies|Kids & Family',
4600             4411 => 'Movies|Musicals',
4601             4412 => 'Movies|Romance',
4602             4413 => 'Movies|Sci-Fi & Fantasy',
4603             4414 => 'Movies|Short Films',
4604             4415 => 'Movies|Special Interest',
4605             4416 => 'Movies|Thriller',
4606             4417 => 'Movies|Sports',
4607             4418 => 'Movies|Western',
4608             4419 => 'Movies|Urban',
4609             4420 => 'Movies|Holiday',
4610             4421 => 'Movies|Made for TV',
4611             4422 => 'Movies|Concert Films',
4612             4423 => 'Movies|Music Documentaries',
4613             4424 => 'Movies|Music Feature Films',
4614             4425 => 'Movies|Japanese Cinema',
4615             4426 => 'Movies|Jidaigeki',
4616             4427 => 'Movies|Tokusatsu',
4617             4428 => 'Movies|Korean Cinema',
4618             4429 => 'Movies|Russian',
4619             4430 => 'Movies|Turkish',
4620             4431 => 'Movies|Bollywood',
4621             4432 => 'Movies|Regional Indian',
4622             4433 => 'Movies|Middle Eastern',
4623             4434 => 'Movies|African',
4624             6000 => 'App Store|Business',
4625             6001 => 'App Store|Weather',
4626             6002 => 'App Store|Utilities',
4627             6003 => 'App Store|Travel',
4628             6004 => 'App Store|Sports',
4629             6005 => 'App Store|Social Networking',
4630             6006 => 'App Store|Reference',
4631             6007 => 'App Store|Productivity',
4632             6008 => 'App Store|Photo & Video',
4633             6009 => 'App Store|News',
4634             6010 => 'App Store|Navigation',
4635             6011 => 'App Store|Music',
4636             6012 => 'App Store|Lifestyle',
4637             6013 => 'App Store|Health & Fitness',
4638             6014 => 'App Store|Games',
4639             6015 => 'App Store|Finance',
4640             6016 => 'App Store|Entertainment',
4641             6017 => 'App Store|Education',
4642             6018 => 'App Store|Books',
4643             6020 => 'App Store|Medical',
4644             6021 => 'App Store|Magazines & Newspapers',
4645             6022 => 'App Store|Catalogs',
4646             6023 => 'App Store|Food & Drink',
4647             6024 => 'App Store|Shopping',
4648             6025 => 'App Store|Stickers',
4649             6026 => 'App Store|Developer Tools',
4650             6027 => 'App Store|Graphics & Design',
4651             7001 => 'App Store|Games|Action',
4652             7002 => 'App Store|Games|Adventure',
4653             7003 => 'App Store|Games|Casual',
4654             7004 => 'App Store|Games|Board',
4655             7005 => 'App Store|Games|Card',
4656             7006 => 'App Store|Games|Casino',
4657             7007 => 'App Store|Games|Dice',
4658             7008 => 'App Store|Games|Educational',
4659             7009 => 'App Store|Games|Family',
4660             7011 => 'App Store|Games|Music',
4661             7012 => 'App Store|Games|Puzzle',
4662             7013 => 'App Store|Games|Racing',
4663             7014 => 'App Store|Games|Role Playing',
4664             7015 => 'App Store|Games|Simulation',
4665             7016 => 'App Store|Games|Sports',
4666             7017 => 'App Store|Games|Strategy',
4667             7018 => 'App Store|Games|Trivia',
4668             7019 => 'App Store|Games|Word',
4669             8001 => 'Tones|Ringtones|Alternative',
4670             8002 => 'Tones|Ringtones|Blues',
4671             8003 => "Tones|Ringtones|Children's Music",
4672             8004 => 'Tones|Ringtones|Classical',
4673             8005 => 'Tones|Ringtones|Comedy',
4674             8006 => 'Tones|Ringtones|Country',
4675             8007 => 'Tones|Ringtones|Dance',
4676             8008 => 'Tones|Ringtones|Electronic',
4677             8009 => 'Tones|Ringtones|Enka',
4678             8010 => 'Tones|Ringtones|French Pop',
4679             8011 => 'Tones|Ringtones|German Folk',
4680             8012 => 'Tones|Ringtones|German Pop',
4681             8013 => 'Tones|Ringtones|Hip-Hop/Rap',
4682             8014 => 'Tones|Ringtones|Holiday',
4683             8015 => 'Tones|Ringtones|Inspirational',
4684             8016 => 'Tones|Ringtones|J-Pop',
4685             8017 => 'Tones|Ringtones|Jazz',
4686             8018 => 'Tones|Ringtones|Kayokyoku',
4687             8019 => 'Tones|Ringtones|Latin',
4688             8020 => 'Tones|Ringtones|New Age',
4689             8021 => 'Tones|Ringtones|Classical|Opera',
4690             8022 => 'Tones|Ringtones|Pop',
4691             8023 => 'Tones|Ringtones|R&B/Soul',
4692             8024 => 'Tones|Ringtones|Reggae',
4693             8025 => 'Tones|Ringtones|Rock',
4694             8026 => 'Tones|Ringtones|Singer/Songwriter',
4695             8027 => 'Tones|Ringtones|Soundtrack',
4696             8028 => 'Tones|Ringtones|Spoken Word',
4697             8029 => 'Tones|Ringtones|Vocal',
4698             8030 => 'Tones|Ringtones|World',
4699             8050 => 'Tones|Alert Tones|Sound Effects',
4700             8051 => 'Tones|Alert Tones|Dialogue',
4701             8052 => 'Tones|Alert Tones|Music',
4702             8053 => 'Tones|Ringtones',
4703             8054 => 'Tones|Alert Tones',
4704             8055 => 'Tones|Ringtones|Alternative|Chinese Alt',
4705             8056 => 'Tones|Ringtones|Alternative|College Rock',
4706             8057 => 'Tones|Ringtones|Alternative|Goth Rock',
4707             8058 => 'Tones|Ringtones|Alternative|Grunge',
4708             8059 => 'Tones|Ringtones|Alternative|Indie Rock',
4709             8060 => 'Tones|Ringtones|Alternative|Korean Indie',
4710             8061 => 'Tones|Ringtones|Alternative|New Wave',
4711             8062 => 'Tones|Ringtones|Alternative|Punk',
4712             8063 => 'Tones|Ringtones|Anime',
4713             8064 => 'Tones|Ringtones|Arabic',
4714             8065 => 'Tones|Ringtones|Arabic|Arabic Pop',
4715             8066 => 'Tones|Ringtones|Arabic|Islamic',
4716             8067 => 'Tones|Ringtones|Arabic|Khaleeji',
4717             8068 => 'Tones|Ringtones|Arabic|North African',
4718             8069 => 'Tones|Ringtones|Blues|Acoustic Blues',
4719             8070 => 'Tones|Ringtones|Blues|Chicago Blues',
4720             8071 => 'Tones|Ringtones|Blues|Classic Blues',
4721             8072 => 'Tones|Ringtones|Blues|Contemporary Blues',
4722             8073 => 'Tones|Ringtones|Blues|Country Blues',
4723             8074 => 'Tones|Ringtones|Blues|Delta Blues',
4724             8075 => 'Tones|Ringtones|Blues|Electric Blues',
4725             8076 => 'Tones|Ringtones|Brazilian',
4726             8077 => 'Tones|Ringtones|Brazilian|Axe', # (Axé)
4727             8078 => 'Tones|Ringtones|Brazilian|Baile Funk',
4728             8079 => 'Tones|Ringtones|Brazilian|Bossa Nova',
4729             8080 => 'Tones|Ringtones|Brazilian|Choro',
4730             8081 => 'Tones|Ringtones|Brazilian|Forro', # (Forró)
4731             8082 => 'Tones|Ringtones|Brazilian|Frevo',
4732             8083 => 'Tones|Ringtones|Brazilian|MPB',
4733             8084 => 'Tones|Ringtones|Brazilian|Pagode',
4734             8085 => 'Tones|Ringtones|Brazilian|Samba',
4735             8086 => 'Tones|Ringtones|Brazilian|Sertanejo',
4736             8087 => "Tones|Ringtones|Children's Music|Lullabies",
4737             8088 => "Tones|Ringtones|Children's Music|Sing-Along",
4738             8089 => "Tones|Ringtones|Children's Music|Stories",
4739             8090 => 'Tones|Ringtones|Chinese',
4740             8091 => 'Tones|Ringtones|Chinese|Chinese Classical',
4741             8092 => 'Tones|Ringtones|Chinese|Chinese Flute',
4742             8093 => 'Tones|Ringtones|Chinese|Chinese Opera',
4743             8094 => 'Tones|Ringtones|Chinese|Chinese Orchestral',
4744             8095 => 'Tones|Ringtones|Chinese|Chinese Regional Folk',
4745             8096 => 'Tones|Ringtones|Chinese|Chinese Strings',
4746             8097 => 'Tones|Ringtones|Chinese|Taiwanese Folk',
4747             8098 => 'Tones|Ringtones|Chinese|Tibetan Native Music',
4748             8099 => 'Tones|Ringtones|Christian & Gospel',
4749             8100 => 'Tones|Ringtones|Christian & Gospel|CCM',
4750             8101 => 'Tones|Ringtones|Christian & Gospel|Christian Metal',
4751             8102 => 'Tones|Ringtones|Christian & Gospel|Christian Pop',
4752             8103 => 'Tones|Ringtones|Christian & Gospel|Christian Rap',
4753             8104 => 'Tones|Ringtones|Christian & Gospel|Christian Rock',
4754             8105 => 'Tones|Ringtones|Christian & Gospel|Classic Christian',
4755             8106 => 'Tones|Ringtones|Christian & Gospel|Contemporary Gospel',
4756             8107 => 'Tones|Ringtones|Christian & Gospel|Gospel',
4757             8108 => 'Tones|Ringtones|Christian & Gospel|Praise & Worship',
4758             8109 => 'Tones|Ringtones|Christian & Gospel|Southern Gospel',
4759             8110 => 'Tones|Ringtones|Christian & Gospel|Traditional Gospel',
4760             8111 => 'Tones|Ringtones|Classical|Avant-Garde',
4761             8112 => 'Tones|Ringtones|Classical|Baroque Era',
4762             8113 => 'Tones|Ringtones|Classical|Chamber Music',
4763             8114 => 'Tones|Ringtones|Classical|Chant',
4764             8115 => 'Tones|Ringtones|Classical|Choral',
4765             8116 => 'Tones|Ringtones|Classical|Classical Crossover',
4766             8117 => 'Tones|Ringtones|Classical|Early Music',
4767             8118 => 'Tones|Ringtones|Classical|High Classical',
4768             8119 => 'Tones|Ringtones|Classical|Impressionist',
4769             8120 => 'Tones|Ringtones|Classical|Medieval Era',
4770             8121 => 'Tones|Ringtones|Classical|Minimalism',
4771             8122 => 'Tones|Ringtones|Classical|Modern Era',
4772             8123 => 'Tones|Ringtones|Classical|Orchestral',
4773             8124 => 'Tones|Ringtones|Classical|Renaissance',
4774             8125 => 'Tones|Ringtones|Classical|Romantic Era',
4775             8126 => 'Tones|Ringtones|Classical|Wedding Music',
4776             8127 => 'Tones|Ringtones|Comedy|Novelty',
4777             8128 => 'Tones|Ringtones|Comedy|Standup Comedy',
4778             8129 => 'Tones|Ringtones|Country|Alternative Country',
4779             8130 => 'Tones|Ringtones|Country|Americana',
4780             8131 => 'Tones|Ringtones|Country|Bluegrass',
4781             8132 => 'Tones|Ringtones|Country|Contemporary Bluegrass',
4782             8133 => 'Tones|Ringtones|Country|Contemporary Country',
4783             8134 => 'Tones|Ringtones|Country|Country Gospel',
4784             8135 => 'Tones|Ringtones|Country|Honky Tonk',
4785             8136 => 'Tones|Ringtones|Country|Outlaw Country',
4786             8137 => 'Tones|Ringtones|Country|Thai Country',
4787             8138 => 'Tones|Ringtones|Country|Traditional Bluegrass',
4788             8139 => 'Tones|Ringtones|Country|Traditional Country',
4789             8140 => 'Tones|Ringtones|Country|Urban Cowboy',
4790             8141 => 'Tones|Ringtones|Dance|Breakbeat',
4791             8142 => 'Tones|Ringtones|Dance|Exercise',
4792             8143 => 'Tones|Ringtones|Dance|Garage',
4793             8144 => 'Tones|Ringtones|Dance|Hardcore',
4794             8145 => 'Tones|Ringtones|Dance|House',
4795             8146 => "Tones|Ringtones|Dance|Jungle/Drum'n'bass",
4796             8147 => 'Tones|Ringtones|Dance|Techno',
4797             8148 => 'Tones|Ringtones|Dance|Trance',
4798             8149 => 'Tones|Ringtones|Disney',
4799             8150 => 'Tones|Ringtones|Easy Listening',
4800             8151 => 'Tones|Ringtones|Easy Listening|Lounge',
4801             8152 => 'Tones|Ringtones|Easy Listening|Swing',
4802             8153 => 'Tones|Ringtones|Electronic|Ambient',
4803             8154 => 'Tones|Ringtones|Electronic|Downtempo',
4804             8155 => 'Tones|Ringtones|Electronic|Electronica',
4805             8156 => 'Tones|Ringtones|Electronic|IDM/Experimental',
4806             8157 => 'Tones|Ringtones|Electronic|Industrial',
4807             8158 => 'Tones|Ringtones|Fitness & Workout',
4808             8159 => 'Tones|Ringtones|Folk',
4809             8160 => 'Tones|Ringtones|Hip-Hop/Rap|Alternative Rap',
4810             8161 => 'Tones|Ringtones|Hip-Hop/Rap|Chinese Hip-Hop',
4811             8162 => 'Tones|Ringtones|Hip-Hop/Rap|Dirty South',
4812             8163 => 'Tones|Ringtones|Hip-Hop/Rap|East Coast Rap',
4813             8164 => 'Tones|Ringtones|Hip-Hop/Rap|Gangsta Rap',
4814             8165 => 'Tones|Ringtones|Hip-Hop/Rap|Hardcore Rap',
4815             8166 => 'Tones|Ringtones|Hip-Hop/Rap|Hip-Hop',
4816             8167 => 'Tones|Ringtones|Hip-Hop/Rap|Korean Hip-Hop',
4817             8168 => 'Tones|Ringtones|Hip-Hop/Rap|Latin Rap',
4818             8169 => 'Tones|Ringtones|Hip-Hop/Rap|Old School Rap',
4819             8170 => 'Tones|Ringtones|Hip-Hop/Rap|Rap',
4820             8171 => 'Tones|Ringtones|Hip-Hop/Rap|Underground Rap',
4821             8172 => 'Tones|Ringtones|Hip-Hop/Rap|West Coast Rap',
4822             8173 => 'Tones|Ringtones|Holiday|Chanukah',
4823             8174 => 'Tones|Ringtones|Holiday|Christmas',
4824             8175 => "Tones|Ringtones|Holiday|Christmas: Children's",
4825             8176 => 'Tones|Ringtones|Holiday|Christmas: Classic',
4826             8177 => 'Tones|Ringtones|Holiday|Christmas: Classical',
4827             8178 => 'Tones|Ringtones|Holiday|Christmas: Jazz',
4828             8179 => 'Tones|Ringtones|Holiday|Christmas: Modern',
4829             8180 => 'Tones|Ringtones|Holiday|Christmas: Pop',
4830             8181 => 'Tones|Ringtones|Holiday|Christmas: R&B',
4831             8182 => 'Tones|Ringtones|Holiday|Christmas: Religious',
4832             8183 => 'Tones|Ringtones|Holiday|Christmas: Rock',
4833             8184 => 'Tones|Ringtones|Holiday|Easter',
4834             8185 => 'Tones|Ringtones|Holiday|Halloween',
4835             8186 => 'Tones|Ringtones|Holiday|Thanksgiving',
4836             8187 => 'Tones|Ringtones|Indian',
4837             8188 => 'Tones|Ringtones|Indian|Bollywood',
4838             8189 => 'Tones|Ringtones|Indian|Devotional & Spiritual',
4839             8190 => 'Tones|Ringtones|Indian|Ghazals',
4840             8191 => 'Tones|Ringtones|Indian|Indian Classical',
4841             8192 => 'Tones|Ringtones|Indian|Indian Folk',
4842             8193 => 'Tones|Ringtones|Indian|Indian Pop',
4843             8194 => 'Tones|Ringtones|Indian|Regional Indian',
4844             8195 => 'Tones|Ringtones|Indian|Sufi',
4845             8196 => 'Tones|Ringtones|Indian|Regional Indian|Tamil',
4846             8197 => 'Tones|Ringtones|Indian|Regional Indian|Telugu',
4847             8198 => 'Tones|Ringtones|Instrumental',
4848             8199 => 'Tones|Ringtones|Jazz|Avant-Garde Jazz',
4849             8201 => 'Tones|Ringtones|Jazz|Big Band',
4850             8202 => 'Tones|Ringtones|Jazz|Bop',
4851             8203 => 'Tones|Ringtones|Jazz|Contemporary Jazz',
4852             8204 => 'Tones|Ringtones|Jazz|Cool Jazz',
4853             8205 => 'Tones|Ringtones|Jazz|Crossover Jazz',
4854             8206 => 'Tones|Ringtones|Jazz|Dixieland',
4855             8207 => 'Tones|Ringtones|Jazz|Fusion',
4856             8208 => 'Tones|Ringtones|Jazz|Hard Bop',
4857             8209 => 'Tones|Ringtones|Jazz|Latin Jazz',
4858             8210 => 'Tones|Ringtones|Jazz|Mainstream Jazz',
4859             8211 => 'Tones|Ringtones|Jazz|Ragtime',
4860             8212 => 'Tones|Ringtones|Jazz|Smooth Jazz',
4861             8213 => 'Tones|Ringtones|Jazz|Trad Jazz',
4862             8214 => 'Tones|Ringtones|Pop|K-Pop',
4863             8215 => 'Tones|Ringtones|Karaoke',
4864             8216 => 'Tones|Ringtones|Korean',
4865             8217 => 'Tones|Ringtones|Korean|Korean Classical',
4866             8218 => 'Tones|Ringtones|Korean|Korean Trad Instrumental',
4867             8219 => 'Tones|Ringtones|Korean|Korean Trad Song',
4868             8220 => 'Tones|Ringtones|Korean|Korean Trad Theater',
4869             8221 => 'Tones|Ringtones|Latin|Alternative & Rock in Spanish',
4870             8222 => 'Tones|Ringtones|Latin|Baladas y Boleros',
4871             8223 => 'Tones|Ringtones|Latin|Contemporary Latin',
4872             8224 => 'Tones|Ringtones|Latin|Latin Jazz',
4873             8225 => 'Tones|Ringtones|Latin|Latin Urban',
4874             8226 => 'Tones|Ringtones|Latin|Pop in Spanish',
4875             8227 => 'Tones|Ringtones|Latin|Raices',
4876             8228 => 'Tones|Ringtones|Latin|Musica Mexicana', # (Música Mexicana)
4877             8229 => 'Tones|Ringtones|Latin|Salsa y Tropical',
4878             8230 => 'Tones|Ringtones|Marching Bands',
4879             8231 => 'Tones|Ringtones|New Age|Healing',
4880             8232 => 'Tones|Ringtones|New Age|Meditation',
4881             8233 => 'Tones|Ringtones|New Age|Nature',
4882             8234 => 'Tones|Ringtones|New Age|Relaxation',
4883             8235 => 'Tones|Ringtones|New Age|Travel',
4884             8236 => 'Tones|Ringtones|Orchestral',
4885             8237 => 'Tones|Ringtones|Pop|Adult Contemporary',
4886             8238 => 'Tones|Ringtones|Pop|Britpop',
4887             8239 => 'Tones|Ringtones|Pop|C-Pop',
4888             8240 => 'Tones|Ringtones|Pop|Cantopop/HK-Pop',
4889             8241 => 'Tones|Ringtones|Pop|Indo Pop',
4890             8242 => 'Tones|Ringtones|Pop|Korean Folk-Pop',
4891             8243 => 'Tones|Ringtones|Pop|Malaysian Pop',
4892             8244 => 'Tones|Ringtones|Pop|Mandopop',
4893             8245 => 'Tones|Ringtones|Pop|Manilla Sound',
4894             8246 => 'Tones|Ringtones|Pop|Oldies',
4895             8247 => 'Tones|Ringtones|Pop|Original Pilipino Music',
4896             8248 => 'Tones|Ringtones|Pop|Pinoy Pop',
4897             8249 => 'Tones|Ringtones|Pop|Pop/Rock',
4898             8250 => 'Tones|Ringtones|Pop|Soft Rock',
4899             8251 => 'Tones|Ringtones|Pop|Tai-Pop',
4900             8252 => 'Tones|Ringtones|Pop|Teen Pop',
4901             8253 => 'Tones|Ringtones|Pop|Thai Pop',
4902             8254 => 'Tones|Ringtones|R&B/Soul|Contemporary R&B',
4903             8255 => 'Tones|Ringtones|R&B/Soul|Disco',
4904             8256 => 'Tones|Ringtones|R&B/Soul|Doo Wop',
4905             8257 => 'Tones|Ringtones|R&B/Soul|Funk',
4906             8258 => 'Tones|Ringtones|R&B/Soul|Motown',
4907             8259 => 'Tones|Ringtones|R&B/Soul|Neo-Soul',
4908             8260 => 'Tones|Ringtones|R&B/Soul|Soul',
4909             8261 => 'Tones|Ringtones|Reggae|Modern Dancehall',
4910             8262 => 'Tones|Ringtones|Reggae|Dub',
4911             8263 => 'Tones|Ringtones|Reggae|Roots Reggae',
4912             8264 => 'Tones|Ringtones|Reggae|Ska',
4913             8265 => 'Tones|Ringtones|Rock|Adult Alternative',
4914             8266 => 'Tones|Ringtones|Rock|American Trad Rock',
4915             8267 => 'Tones|Ringtones|Rock|Arena Rock',
4916             8268 => 'Tones|Ringtones|Rock|Blues-Rock',
4917             8269 => 'Tones|Ringtones|Rock|British Invasion',
4918             8270 => 'Tones|Ringtones|Rock|Chinese Rock',
4919             8271 => 'Tones|Ringtones|Rock|Death Metal/Black Metal',
4920             8272 => 'Tones|Ringtones|Rock|Glam Rock',
4921             8273 => 'Tones|Ringtones|Rock|Hair Metal',
4922             8274 => 'Tones|Ringtones|Rock|Hard Rock',
4923             8275 => 'Tones|Ringtones|Rock|Metal',
4924             8276 => 'Tones|Ringtones|Rock|Jam Bands',
4925             8277 => 'Tones|Ringtones|Rock|Korean Rock',
4926             8278 => 'Tones|Ringtones|Rock|Prog-Rock/Art Rock',
4927             8279 => 'Tones|Ringtones|Rock|Psychedelic',
4928             8280 => 'Tones|Ringtones|Rock|Rock & Roll',
4929             8281 => 'Tones|Ringtones|Rock|Rockabilly',
4930             8282 => 'Tones|Ringtones|Rock|Roots Rock',
4931             8283 => 'Tones|Ringtones|Rock|Singer/Songwriter',
4932             8284 => 'Tones|Ringtones|Rock|Southern Rock',
4933             8285 => 'Tones|Ringtones|Rock|Surf',
4934             8286 => 'Tones|Ringtones|Rock|Tex-Mex',
4935             8287 => 'Tones|Ringtones|Singer/Songwriter|Alternative Folk',
4936             8288 => 'Tones|Ringtones|Singer/Songwriter|Contemporary Folk',
4937             8289 => 'Tones|Ringtones|Singer/Songwriter|Contemporary Singer/Songwriter',
4938             8290 => 'Tones|Ringtones|Singer/Songwriter|Folk-Rock',
4939             8291 => 'Tones|Ringtones|Singer/Songwriter|New Acoustic',
4940             8292 => 'Tones|Ringtones|Singer/Songwriter|Traditional Folk',
4941             8293 => 'Tones|Ringtones|Soundtrack|Foreign Cinema',
4942             8294 => 'Tones|Ringtones|Soundtrack|Musicals',
4943             8295 => 'Tones|Ringtones|Soundtrack|Original Score',
4944             8296 => 'Tones|Ringtones|Soundtrack|Sound Effects',
4945             8297 => 'Tones|Ringtones|Soundtrack|Soundtrack',
4946             8298 => 'Tones|Ringtones|Soundtrack|TV Soundtrack',
4947             8299 => 'Tones|Ringtones|Vocal|Standards',
4948             8300 => 'Tones|Ringtones|Vocal|Traditional Pop',
4949             8301 => 'Tones|Ringtones|Vocal|Trot',
4950             8302 => 'Tones|Ringtones|Jazz|Vocal Jazz',
4951             8303 => 'Tones|Ringtones|Vocal|Vocal Pop',
4952             8304 => 'Tones|Ringtones|African',
4953             8305 => 'Tones|Ringtones|African|Afrikaans',
4954             8306 => 'Tones|Ringtones|African|Afro-Beat',
4955             8307 => 'Tones|Ringtones|African|Afro-Pop',
4956             8308 => 'Tones|Ringtones|Turkish|Arabesque',
4957             8309 => 'Tones|Ringtones|World|Asia',
4958             8310 => 'Tones|Ringtones|World|Australia',
4959             8311 => 'Tones|Ringtones|World|Cajun',
4960             8312 => 'Tones|Ringtones|World|Calypso',
4961             8313 => 'Tones|Ringtones|World|Caribbean',
4962             8314 => 'Tones|Ringtones|World|Celtic',
4963             8315 => 'Tones|Ringtones|World|Celtic Folk',
4964             8316 => 'Tones|Ringtones|World|Contemporary Celtic',
4965             8317 => 'Tones|Ringtones|World|Dangdut',
4966             8318 => 'Tones|Ringtones|World|Dini',
4967             8319 => 'Tones|Ringtones|World|Europe',
4968             8320 => 'Tones|Ringtones|World|Fado',
4969             8321 => 'Tones|Ringtones|World|Farsi',
4970             8322 => 'Tones|Ringtones|World|Flamenco',
4971             8323 => 'Tones|Ringtones|World|France',
4972             8324 => 'Tones|Ringtones|Turkish|Halk',
4973             8325 => 'Tones|Ringtones|World|Hawaii',
4974             8326 => 'Tones|Ringtones|World|Iberia',
4975             8327 => 'Tones|Ringtones|World|Indonesian Religious',
4976             8328 => 'Tones|Ringtones|World|Israeli',
4977             8329 => 'Tones|Ringtones|World|Japan',
4978             8330 => 'Tones|Ringtones|World|Klezmer',
4979             8331 => 'Tones|Ringtones|World|North America',
4980             8332 => 'Tones|Ringtones|World|Polka',
4981             8333 => 'Tones|Ringtones|Russian',
4982             8334 => 'Tones|Ringtones|Russian|Russian Chanson',
4983             8335 => 'Tones|Ringtones|Turkish|Sanat',
4984             8336 => 'Tones|Ringtones|World|Soca',
4985             8337 => 'Tones|Ringtones|World|South Africa',
4986             8338 => 'Tones|Ringtones|World|South America',
4987             8339 => 'Tones|Ringtones|World|Tango',
4988             8340 => 'Tones|Ringtones|World|Traditional Celtic',
4989             8341 => 'Tones|Ringtones|Turkish',
4990             8342 => 'Tones|Ringtones|World|Worldbeat',
4991             8343 => 'Tones|Ringtones|World|Zydeco',
4992             8345 => 'Tones|Ringtones|Classical|Art Song',
4993             8346 => 'Tones|Ringtones|Classical|Brass & Woodwinds',
4994             8347 => 'Tones|Ringtones|Classical|Solo Instrumental',
4995             8348 => 'Tones|Ringtones|Classical|Contemporary Era',
4996             8349 => 'Tones|Ringtones|Classical|Oratorio',
4997             8350 => 'Tones|Ringtones|Classical|Cantata',
4998             8351 => 'Tones|Ringtones|Classical|Electronic',
4999             8352 => 'Tones|Ringtones|Classical|Sacred',
5000             8353 => 'Tones|Ringtones|Classical|Guitar',
5001             8354 => 'Tones|Ringtones|Classical|Piano',
5002             8355 => 'Tones|Ringtones|Classical|Violin',
5003             8356 => 'Tones|Ringtones|Classical|Cello',
5004             8357 => 'Tones|Ringtones|Classical|Percussion',
5005             8358 => 'Tones|Ringtones|Electronic|Dubstep',
5006             8359 => 'Tones|Ringtones|Electronic|Bass',
5007             8360 => 'Tones|Ringtones|Hip-Hop/Rap|UK Hip Hop',
5008             8361 => 'Tones|Ringtones|Reggae|Lovers Rock',
5009             8362 => 'Tones|Ringtones|Alternative|EMO',
5010             8363 => 'Tones|Ringtones|Alternative|Pop Punk',
5011             8364 => 'Tones|Ringtones|Alternative|Indie Pop',
5012             8365 => 'Tones|Ringtones|New Age|Yoga',
5013             8366 => 'Tones|Ringtones|Pop|Tribute',
5014             8367 => 'Tones|Ringtones|Pop|Shows',
5015             8368 => 'Tones|Ringtones|Cuban',
5016             8369 => 'Tones|Ringtones|Cuban|Mambo',
5017             8370 => 'Tones|Ringtones|Cuban|Chachacha',
5018             8371 => 'Tones|Ringtones|Cuban|Guajira',
5019             8372 => 'Tones|Ringtones|Cuban|Son',
5020             8373 => 'Tones|Ringtones|Cuban|Bolero',
5021             8374 => 'Tones|Ringtones|Cuban|Guaracha',
5022             8375 => 'Tones|Ringtones|Cuban|Timba',
5023             8376 => 'Tones|Ringtones|Soundtrack|Video Game',
5024             8377 => 'Tones|Ringtones|Indian|Regional Indian|Punjabi|Punjabi Pop',
5025             8378 => 'Tones|Ringtones|Indian|Regional Indian|Bengali|Rabindra Sangeet',
5026             8379 => 'Tones|Ringtones|Indian|Regional Indian|Malayalam',
5027             8380 => 'Tones|Ringtones|Indian|Regional Indian|Kannada',
5028             8381 => 'Tones|Ringtones|Indian|Regional Indian|Marathi',
5029             8382 => 'Tones|Ringtones|Indian|Regional Indian|Gujarati',
5030             8383 => 'Tones|Ringtones|Indian|Regional Indian|Assamese',
5031             8384 => 'Tones|Ringtones|Indian|Regional Indian|Bhojpuri',
5032             8385 => 'Tones|Ringtones|Indian|Regional Indian|Haryanvi',
5033             8386 => 'Tones|Ringtones|Indian|Regional Indian|Odia',
5034             8387 => 'Tones|Ringtones|Indian|Regional Indian|Rajasthani',
5035             8388 => 'Tones|Ringtones|Indian|Regional Indian|Urdu',
5036             8389 => 'Tones|Ringtones|Indian|Regional Indian|Punjabi',
5037             8390 => 'Tones|Ringtones|Indian|Regional Indian|Bengali',
5038             8391 => 'Tones|Ringtones|Indian|Indian Classical|Carnatic Classical',
5039             8392 => 'Tones|Ringtones|Indian|Indian Classical|Hindustani Classical',
5040             8393 => 'Tones|Ringtones|African|Afro House',
5041             8394 => 'Tones|Ringtones|African|Afro Soul',
5042             8395 => 'Tones|Ringtones|African|Afrobeats',
5043             8396 => 'Tones|Ringtones|African|Benga',
5044             8397 => 'Tones|Ringtones|African|Bongo-Flava',
5045             8398 => 'Tones|Ringtones|African|Coupe-Decale',
5046             8399 => 'Tones|Ringtones|African|Gqom',
5047             8400 => 'Tones|Ringtones|African|Highlife',
5048             8401 => 'Tones|Ringtones|African|Kuduro',
5049             8402 => 'Tones|Ringtones|African|Kizomba',
5050             8403 => 'Tones|Ringtones|African|Kwaito',
5051             8404 => 'Tones|Ringtones|African|Mbalax',
5052             8405 => 'Tones|Ringtones|African|Ndombolo',
5053             8406 => 'Tones|Ringtones|African|Shangaan Electro',
5054             8407 => 'Tones|Ringtones|African|Soukous',
5055             8408 => 'Tones|Ringtones|African|Taarab',
5056             8409 => 'Tones|Ringtones|African|Zouglou',
5057             8410 => 'Tones|Ringtones|Turkish|Ozgun',
5058             8411 => 'Tones|Ringtones|Turkish|Fantezi',
5059             8412 => 'Tones|Ringtones|Turkish|Religious',
5060             8413 => 'Tones|Ringtones|Pop|Turkish Pop',
5061             8414 => 'Tones|Ringtones|Rock|Turkish Rock',
5062             8415 => 'Tones|Ringtones|Alternative|Turkish Alternative',
5063             8416 => 'Tones|Ringtones|Hip-Hop/Rap|Turkish Hip-Hop/Rap',
5064             8417 => 'Tones|Ringtones|African|Maskandi',
5065             8418 => 'Tones|Ringtones|Russian|Russian Romance',
5066             8419 => 'Tones|Ringtones|Russian|Russian Bard',
5067             8420 => 'Tones|Ringtones|Russian|Russian Pop',
5068             8421 => 'Tones|Ringtones|Russian|Russian Rock',
5069             8422 => 'Tones|Ringtones|Russian|Russian Hip-Hop',
5070             8423 => 'Tones|Ringtones|Arabic|Levant',
5071             8424 => 'Tones|Ringtones|Arabic|Levant|Dabke',
5072             8425 => 'Tones|Ringtones|Arabic|Maghreb Rai',
5073             8426 => 'Tones|Ringtones|Arabic|Khaleeji|Khaleeji Jalsat',
5074             8427 => 'Tones|Ringtones|Arabic|Khaleeji|Khaleeji Shailat',
5075             8428 => 'Tones|Ringtones|Tarab',
5076             8429 => 'Tones|Ringtones|Tarab|Iraqi Tarab',
5077             8430 => 'Tones|Ringtones|Tarab|Egyptian Tarab',
5078             8431 => 'Tones|Ringtones|Tarab|Khaleeji Tarab',
5079             8432 => 'Tones|Ringtones|Pop|Levant Pop',
5080             8433 => 'Tones|Ringtones|Pop|Iraqi Pop',
5081             8434 => 'Tones|Ringtones|Pop|Egyptian Pop',
5082             8435 => 'Tones|Ringtones|Pop|Maghreb Pop',
5083             8436 => 'Tones|Ringtones|Pop|Khaleeji Pop',
5084             8437 => 'Tones|Ringtones|Hip-Hop/Rap|Levant Hip-Hop',
5085             8438 => 'Tones|Ringtones|Hip-Hop/Rap|Egyptian Hip-Hop',
5086             8439 => 'Tones|Ringtones|Hip-Hop/Rap|Maghreb Hip-Hop',
5087             8440 => 'Tones|Ringtones|Hip-Hop/Rap|Khaleeji Hip-Hop',
5088             8441 => 'Tones|Ringtones|Alternative|Indie Levant',
5089             8442 => 'Tones|Ringtones|Alternative|Indie Egyptian',
5090             8443 => 'Tones|Ringtones|Alternative|Indie Maghreb',
5091             8444 => 'Tones|Ringtones|Electronic|Levant Electronic',
5092             8445 => "Tones|Ringtones|Electronic|Electro-Cha'abi",
5093             8446 => 'Tones|Ringtones|Electronic|Maghreb Electronic',
5094             8447 => 'Tones|Ringtones|Folk|Iraqi Folk',
5095             8448 => 'Tones|Ringtones|Folk|Khaleeji Folk',
5096             8449 => 'Tones|Ringtones|Dance|Maghreb Dance',
5097             9002 => 'Books|Nonfiction',
5098             9003 => 'Books|Romance',
5099             9004 => 'Books|Travel & Adventure',
5100             9007 => 'Books|Arts & Entertainment',
5101             9008 => 'Books|Biographies & Memoirs',
5102             9009 => 'Books|Business & Personal Finance',
5103             9010 => 'Books|Children & Teens',
5104             9012 => 'Books|Humor',
5105             9015 => 'Books|History',
5106             9018 => 'Books|Religion & Spirituality',
5107             9019 => 'Books|Science & Nature',
5108             9020 => 'Books|Sci-Fi & Fantasy',
5109             9024 => 'Books|Lifestyle & Home',
5110             9025 => 'Books|Self-Development',
5111             9026 => 'Books|Comics & Graphic Novels',
5112             9027 => 'Books|Computers & Internet',
5113             9028 => 'Books|Cookbooks, Food & Wine',
5114             9029 => 'Books|Professional & Technical',
5115             9030 => 'Books|Parenting',
5116             9031 => 'Books|Fiction & Literature',
5117             9032 => 'Books|Mysteries & Thrillers',
5118             9033 => 'Books|Reference',
5119             9034 => 'Books|Politics & Current Events',
5120             9035 => 'Books|Sports & Outdoors',
5121             10001 => 'Books|Lifestyle & Home|Antiques & Collectibles',
5122             10002 => 'Books|Arts & Entertainment|Art & Architecture',
5123             10003 => 'Books|Religion & Spirituality|Bibles',
5124             10004 => 'Books|Self-Development|Spirituality',
5125             10005 => 'Books|Business & Personal Finance|Industries & Professions',
5126             10006 => 'Books|Business & Personal Finance|Marketing & Sales',
5127             10007 => 'Books|Business & Personal Finance|Small Business & Entrepreneurship',
5128             10008 => 'Books|Business & Personal Finance|Personal Finance',
5129             10009 => 'Books|Business & Personal Finance|Reference',
5130             10010 => 'Books|Business & Personal Finance|Careers',
5131             10011 => 'Books|Business & Personal Finance|Economics',
5132             10012 => 'Books|Business & Personal Finance|Investing',
5133             10013 => 'Books|Business & Personal Finance|Finance',
5134             10014 => 'Books|Business & Personal Finance|Management & Leadership',
5135             10015 => 'Books|Comics & Graphic Novels|Graphic Novels',
5136             10016 => 'Books|Comics & Graphic Novels|Manga',
5137             10017 => 'Books|Computers & Internet|Computers',
5138             10018 => 'Books|Computers & Internet|Databases',
5139             10019 => 'Books|Computers & Internet|Digital Media',
5140             10020 => 'Books|Computers & Internet|Internet',
5141             10021 => 'Books|Computers & Internet|Network',
5142             10022 => 'Books|Computers & Internet|Operating Systems',
5143             10023 => 'Books|Computers & Internet|Programming',
5144             10024 => 'Books|Computers & Internet|Software',
5145             10025 => 'Books|Computers & Internet|System Administration',
5146             10026 => 'Books|Cookbooks, Food & Wine|Beverages',
5147             10027 => 'Books|Cookbooks, Food & Wine|Courses & Dishes',
5148             10028 => 'Books|Cookbooks, Food & Wine|Special Diet',
5149             10029 => 'Books|Cookbooks, Food & Wine|Special Occasions',
5150             10030 => 'Books|Cookbooks, Food & Wine|Methods',
5151             10031 => 'Books|Cookbooks, Food & Wine|Reference',
5152             10032 => 'Books|Cookbooks, Food & Wine|Regional & Ethnic',
5153             10033 => 'Books|Cookbooks, Food & Wine|Specific Ingredients',
5154             10034 => 'Books|Lifestyle & Home|Crafts & Hobbies',
5155             10035 => 'Books|Professional & Technical|Design',
5156             10036 => 'Books|Arts & Entertainment|Theater',
5157             10037 => 'Books|Professional & Technical|Education',
5158             10038 => 'Books|Nonfiction|Family & Relationships',
5159             10039 => 'Books|Fiction & Literature|Action & Adventure',
5160             10040 => 'Books|Fiction & Literature|African American',
5161             10041 => 'Books|Fiction & Literature|Religious',
5162             10042 => 'Books|Fiction & Literature|Classics',
5163             10043 => 'Books|Fiction & Literature|Erotica',
5164             10044 => 'Books|Sci-Fi & Fantasy|Fantasy',
5165             10045 => 'Books|Fiction & Literature|Gay',
5166             10046 => 'Books|Fiction & Literature|Ghost',
5167             10047 => 'Books|Fiction & Literature|Historical',
5168             10048 => 'Books|Fiction & Literature|Horror',
5169             10049 => 'Books|Fiction & Literature|Literary',
5170             10050 => 'Books|Mysteries & Thrillers|Hard-Boiled',
5171             10051 => 'Books|Mysteries & Thrillers|Historical',
5172             10052 => 'Books|Mysteries & Thrillers|Police Procedural',
5173             10053 => 'Books|Mysteries & Thrillers|Short Stories',
5174             10054 => 'Books|Mysteries & Thrillers|British Detectives',
5175             10055 => 'Books|Mysteries & Thrillers|Women Sleuths',
5176             10056 => 'Books|Romance|Erotic Romance',
5177             10057 => 'Books|Romance|Contemporary',
5178             10058 => 'Books|Romance|Paranormal',
5179             10059 => 'Books|Romance|Historical',
5180             10060 => 'Books|Romance|Short Stories',
5181             10061 => 'Books|Romance|Suspense',
5182             10062 => 'Books|Romance|Western',
5183             10063 => 'Books|Sci-Fi & Fantasy|Science Fiction',
5184             10064 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature',
5185             10065 => 'Books|Fiction & Literature|Short Stories',
5186             10066 => 'Books|Reference|Foreign Languages',
5187             10067 => 'Books|Arts & Entertainment|Games',
5188             10068 => 'Books|Lifestyle & Home|Gardening',
5189             10069 => 'Books|Self-Development|Health & Fitness',
5190             10070 => 'Books|History|Africa',
5191             10071 => 'Books|History|Americas',
5192             10072 => 'Books|History|Ancient',
5193             10073 => 'Books|History|Asia',
5194             10074 => 'Books|History|Australia & Oceania',
5195             10075 => 'Books|History|Europe',
5196             10076 => 'Books|History|Latin America',
5197             10077 => 'Books|History|Middle East',
5198             10078 => 'Books|History|Military',
5199             10079 => 'Books|History|United States',
5200             10080 => 'Books|History|World',
5201             10081 => "Books|Children & Teens|Children's Fiction",
5202             10082 => "Books|Children & Teens|Children's Nonfiction",
5203             10083 => 'Books|Professional & Technical|Law',
5204             10084 => 'Books|Fiction & Literature|Literary Criticism',
5205             10085 => 'Books|Science & Nature|Mathematics',
5206             10086 => 'Books|Professional & Technical|Medical',
5207             10087 => 'Books|Arts & Entertainment|Music',
5208             10088 => 'Books|Science & Nature|Nature',
5209             10089 => 'Books|Arts & Entertainment|Performing Arts',
5210             10090 => 'Books|Lifestyle & Home|Pets',
5211             10091 => 'Books|Nonfiction|Philosophy',
5212             10092 => 'Books|Arts & Entertainment|Photography',
5213             10093 => 'Books|Fiction & Literature|Poetry',
5214             10094 => 'Books|Self-Development|Psychology',
5215             10095 => 'Books|Reference|Almanacs & Yearbooks',
5216             10096 => 'Books|Reference|Atlases & Maps',
5217             10097 => 'Books|Reference|Catalogs & Directories',
5218             10098 => 'Books|Reference|Consumer Guides',
5219             10099 => 'Books|Reference|Dictionaries & Thesauruses',
5220             10100 => 'Books|Reference|Encyclopedias',
5221             10101 => 'Books|Reference|Etiquette',
5222             10102 => 'Books|Reference|Quotations',
5223             10103 => 'Books|Reference|Words & Language',
5224             10104 => 'Books|Reference|Writing',
5225             10105 => 'Books|Religion & Spirituality|Bible Studies',
5226             10106 => 'Books|Religion & Spirituality|Buddhism',
5227             10107 => 'Books|Religion & Spirituality|Christianity',
5228             10108 => 'Books|Religion & Spirituality|Hinduism',
5229             10109 => 'Books|Religion & Spirituality|Islam',
5230             10110 => 'Books|Religion & Spirituality|Judaism',
5231             10111 => 'Books|Science & Nature|Astronomy',
5232             10112 => 'Books|Science & Nature|Chemistry',
5233             10113 => 'Books|Science & Nature|Earth Sciences',
5234             10114 => 'Books|Science & Nature|Essays',
5235             10115 => 'Books|Science & Nature|History',
5236             10116 => 'Books|Science & Nature|Life Sciences',
5237             10117 => 'Books|Science & Nature|Physics',
5238             10118 => 'Books|Science & Nature|Reference',
5239             10119 => 'Books|Self-Development|Self-Improvement',
5240             10120 => 'Books|Nonfiction|Social Science',
5241             10121 => 'Books|Sports & Outdoors|Baseball',
5242             10122 => 'Books|Sports & Outdoors|Basketball',
5243             10123 => 'Books|Sports & Outdoors|Coaching',
5244             10124 => 'Books|Sports & Outdoors|Extreme Sports',
5245             10125 => 'Books|Sports & Outdoors|Football',
5246             10126 => 'Books|Sports & Outdoors|Golf',
5247             10127 => 'Books|Sports & Outdoors|Hockey',
5248             10128 => 'Books|Sports & Outdoors|Mountaineering',
5249             10129 => 'Books|Sports & Outdoors|Outdoors',
5250             10130 => 'Books|Sports & Outdoors|Racket Sports',
5251             10131 => 'Books|Sports & Outdoors|Reference',
5252             10132 => 'Books|Sports & Outdoors|Soccer',
5253             10133 => 'Books|Sports & Outdoors|Training',
5254             10134 => 'Books|Sports & Outdoors|Water Sports',
5255             10135 => 'Books|Sports & Outdoors|Winter Sports',
5256             10136 => 'Books|Reference|Study Aids',
5257             10137 => 'Books|Professional & Technical|Engineering',
5258             10138 => 'Books|Nonfiction|Transportation',
5259             10139 => 'Books|Travel & Adventure|Africa',
5260             10140 => 'Books|Travel & Adventure|Asia',
5261             10141 => 'Books|Travel & Adventure|Specialty Travel',
5262             10142 => 'Books|Travel & Adventure|Canada',
5263             10143 => 'Books|Travel & Adventure|Caribbean',
5264             10144 => 'Books|Travel & Adventure|Latin America',
5265             10145 => 'Books|Travel & Adventure|Essays & Memoirs',
5266             10146 => 'Books|Travel & Adventure|Europe',
5267             10147 => 'Books|Travel & Adventure|Middle East',
5268             10148 => 'Books|Travel & Adventure|United States',
5269             10149 => 'Books|Nonfiction|True Crime',
5270             11001 => 'Books|Sci-Fi & Fantasy|Fantasy|Contemporary',
5271             11002 => 'Books|Sci-Fi & Fantasy|Fantasy|Epic',
5272             11003 => 'Books|Sci-Fi & Fantasy|Fantasy|Historical',
5273             11004 => 'Books|Sci-Fi & Fantasy|Fantasy|Paranormal',
5274             11005 => 'Books|Sci-Fi & Fantasy|Fantasy|Short Stories',
5275             11006 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature|Adventure',
5276             11007 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature|High Tech',
5277             11008 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature|Short Stories',
5278             11009 => 'Books|Professional & Technical|Education|Language Arts & Disciplines',
5279             11010 => 'Books|Communications & Media',
5280             11011 => 'Books|Communications & Media|Broadcasting',
5281             11012 => 'Books|Communications & Media|Digital Media',
5282             11013 => 'Books|Communications & Media|Journalism',
5283             11014 => 'Books|Communications & Media|Photojournalism',
5284             11015 => 'Books|Communications & Media|Print',
5285             11016 => 'Books|Communications & Media|Speech',
5286             11017 => 'Books|Communications & Media|Writing',
5287             11018 => 'Books|Arts & Entertainment|Art & Architecture|Urban Planning',
5288             11019 => 'Books|Arts & Entertainment|Dance',
5289             11020 => 'Books|Arts & Entertainment|Fashion',
5290             11021 => 'Books|Arts & Entertainment|Film',
5291             11022 => 'Books|Arts & Entertainment|Interior Design',
5292             11023 => 'Books|Arts & Entertainment|Media Arts',
5293             11024 => 'Books|Arts & Entertainment|Radio',
5294             11025 => 'Books|Arts & Entertainment|TV',
5295             11026 => 'Books|Arts & Entertainment|Visual Arts',
5296             11027 => 'Books|Biographies & Memoirs|Arts & Entertainment',
5297             11028 => 'Books|Biographies & Memoirs|Business',
5298             11029 => 'Books|Biographies & Memoirs|Culinary',
5299             11030 => 'Books|Biographies & Memoirs|Gay & Lesbian',
5300             11031 => 'Books|Biographies & Memoirs|Historical',
5301             11032 => 'Books|Biographies & Memoirs|Literary',
5302             11033 => 'Books|Biographies & Memoirs|Media & Journalism',
5303             11034 => 'Books|Biographies & Memoirs|Military',
5304             11035 => 'Books|Biographies & Memoirs|Politics',
5305             11036 => 'Books|Biographies & Memoirs|Religious',
5306             11037 => 'Books|Biographies & Memoirs|Science & Technology',
5307             11038 => 'Books|Biographies & Memoirs|Sports',
5308             11039 => 'Books|Biographies & Memoirs|Women',
5309             11040 => 'Books|Romance|New Adult',
5310             11042 => 'Books|Romance|Romantic Comedy',
5311             11043 => 'Books|Romance|Gay & Lesbian',
5312             11044 => 'Books|Fiction & Literature|Essays',
5313             11045 => 'Books|Fiction & Literature|Anthologies',
5314             11046 => 'Books|Fiction & Literature|Comparative Literature',
5315             11047 => 'Books|Fiction & Literature|Drama',
5316             11049 => 'Books|Fiction & Literature|Fairy Tales, Myths & Fables',
5317             11050 => 'Books|Fiction & Literature|Family',
5318             11051 => 'Books|Comics & Graphic Novels|Manga|School Drama',
5319             11052 => 'Books|Comics & Graphic Novels|Manga|Human Drama',
5320             11053 => 'Books|Comics & Graphic Novels|Manga|Family Drama',
5321             11054 => 'Books|Sports & Outdoors|Boxing',
5322             11055 => 'Books|Sports & Outdoors|Cricket',
5323             11056 => 'Books|Sports & Outdoors|Cycling',
5324             11057 => 'Books|Sports & Outdoors|Equestrian',
5325             11058 => 'Books|Sports & Outdoors|Martial Arts & Self Defense',
5326             11059 => 'Books|Sports & Outdoors|Motor Sports',
5327             11060 => 'Books|Sports & Outdoors|Rugby',
5328             11061 => 'Books|Sports & Outdoors|Running',
5329             11062 => 'Books|Self-Development|Diet & Nutrition',
5330             11063 => 'Books|Science & Nature|Agriculture',
5331             11064 => 'Books|Science & Nature|Atmosphere',
5332             11065 => 'Books|Science & Nature|Biology',
5333             11066 => 'Books|Science & Nature|Ecology',
5334             11067 => 'Books|Science & Nature|Environment',
5335             11068 => 'Books|Science & Nature|Geography',
5336             11069 => 'Books|Science & Nature|Geology',
5337             11070 => 'Books|Nonfiction|Social Science|Anthropology',
5338             11071 => 'Books|Nonfiction|Social Science|Archaeology',
5339             11072 => 'Books|Nonfiction|Social Science|Civics',
5340             11073 => 'Books|Nonfiction|Social Science|Government',
5341             11074 => 'Books|Nonfiction|Social Science|Social Studies',
5342             11075 => 'Books|Nonfiction|Social Science|Social Welfare',
5343             11076 => 'Books|Nonfiction|Social Science|Society',
5344             11077 => 'Books|Nonfiction|Philosophy|Aesthetics',
5345             11078 => 'Books|Nonfiction|Philosophy|Epistemology',
5346             11079 => 'Books|Nonfiction|Philosophy|Ethics',
5347             11080 => 'Books|Nonfiction|Philosophy|Language',
5348             11081 => 'Books|Nonfiction|Philosophy|Logic',
5349             11082 => 'Books|Nonfiction|Philosophy|Metaphysics',
5350             11083 => 'Books|Nonfiction|Philosophy|Political',
5351             11084 => 'Books|Nonfiction|Philosophy|Religion',
5352             11085 => 'Books|Reference|Manuals',
5353             11086 => 'Books|Kids',
5354             11087 => 'Books|Kids|Animals',
5355             11088 => 'Books|Kids|Basic Concepts',
5356             11089 => 'Books|Kids|Basic Concepts|Alphabet',
5357             11090 => 'Books|Kids|Basic Concepts|Body',
5358             11091 => 'Books|Kids|Basic Concepts|Colors',
5359             11092 => 'Books|Kids|Basic Concepts|Counting & Numbers',
5360             11093 => 'Books|Kids|Basic Concepts|Date & Time',
5361             11094 => 'Books|Kids|Basic Concepts|General',
5362             11095 => 'Books|Kids|Basic Concepts|Money',
5363             11096 => 'Books|Kids|Basic Concepts|Opposites',
5364             11097 => 'Books|Kids|Basic Concepts|Seasons',
5365             11098 => 'Books|Kids|Basic Concepts|Senses & Sensation',
5366             11099 => 'Books|Kids|Basic Concepts|Size & Shape',
5367             11100 => 'Books|Kids|Basic Concepts|Sounds',
5368             11101 => 'Books|Kids|Basic Concepts|Words',
5369             11102 => 'Books|Kids|Biography',
5370             11103 => 'Books|Kids|Careers & Occupations',
5371             11104 => 'Books|Kids|Computers & Technology',
5372             11105 => 'Books|Kids|Cooking & Food',
5373             11106 => 'Books|Kids|Arts & Entertainment',
5374             11107 => 'Books|Kids|Arts & Entertainment|Art',
5375             11108 => 'Books|Kids|Arts & Entertainment|Crafts',
5376             11109 => 'Books|Kids|Arts & Entertainment|Music',
5377             11110 => 'Books|Kids|Arts & Entertainment|Performing Arts',
5378             11111 => 'Books|Kids|Family',
5379             11112 => 'Books|Kids|Fiction',
5380             11113 => 'Books|Kids|Fiction|Action & Adventure',
5381             11114 => 'Books|Kids|Fiction|Animals',
5382             11115 => 'Books|Kids|Fiction|Classics',
5383             11116 => 'Books|Kids|Fiction|Comics & Graphic Novels',
5384             11117 => 'Books|Kids|Fiction|Culture, Places & People',
5385             11118 => 'Books|Kids|Fiction|Family & Relationships',
5386             11119 => 'Books|Kids|Fiction|Fantasy',
5387             11120 => 'Books|Kids|Fiction|Fairy Tales, Myths & Fables',
5388             11121 => 'Books|Kids|Fiction|Favorite Characters',
5389             11122 => 'Books|Kids|Fiction|Historical',
5390             11123 => 'Books|Kids|Fiction|Holidays & Celebrations',
5391             11124 => 'Books|Kids|Fiction|Monsters & Ghosts',
5392             11125 => 'Books|Kids|Fiction|Mysteries',
5393             11126 => 'Books|Kids|Fiction|Nature',
5394             11127 => 'Books|Kids|Fiction|Religion',
5395             11128 => 'Books|Kids|Fiction|Sci-Fi',
5396             11129 => 'Books|Kids|Fiction|Social Issues',
5397             11130 => 'Books|Kids|Fiction|Sports & Recreation',
5398             11131 => 'Books|Kids|Fiction|Transportation',
5399             11132 => 'Books|Kids|Games & Activities',
5400             11133 => 'Books|Kids|General Nonfiction',
5401             11134 => 'Books|Kids|Health',
5402             11135 => 'Books|Kids|History',
5403             11136 => 'Books|Kids|Holidays & Celebrations',
5404             11137 => 'Books|Kids|Holidays & Celebrations|Birthdays',
5405             11138 => 'Books|Kids|Holidays & Celebrations|Christmas & Advent',
5406             11139 => 'Books|Kids|Holidays & Celebrations|Easter & Lent',
5407             11140 => 'Books|Kids|Holidays & Celebrations|General',
5408             11141 => 'Books|Kids|Holidays & Celebrations|Halloween',
5409             11142 => 'Books|Kids|Holidays & Celebrations|Hanukkah',
5410             11143 => 'Books|Kids|Holidays & Celebrations|Other',
5411             11144 => 'Books|Kids|Holidays & Celebrations|Passover',
5412             11145 => 'Books|Kids|Holidays & Celebrations|Patriotic Holidays',
5413             11146 => 'Books|Kids|Holidays & Celebrations|Ramadan',
5414             11147 => 'Books|Kids|Holidays & Celebrations|Thanksgiving',
5415             11148 => "Books|Kids|Holidays & Celebrations|Valentine's Day",
5416             11149 => 'Books|Kids|Humor',
5417             11150 => 'Books|Kids|Humor|Jokes & Riddles',
5418             11151 => 'Books|Kids|Poetry',
5419             11152 => 'Books|Kids|Learning to Read',
5420             11153 => 'Books|Kids|Learning to Read|Chapter Books',
5421             11154 => 'Books|Kids|Learning to Read|Early Readers',
5422             11155 => 'Books|Kids|Learning to Read|Intermediate Readers',
5423             11156 => 'Books|Kids|Nursery Rhymes',
5424             11157 => 'Books|Kids|Government',
5425             11158 => 'Books|Kids|Reference',
5426             11159 => 'Books|Kids|Religion',
5427             11160 => 'Books|Kids|Science & Nature',
5428             11161 => 'Books|Kids|Social Issues',
5429             11162 => 'Books|Kids|Social Studies',
5430             11163 => 'Books|Kids|Sports & Recreation',
5431             11164 => 'Books|Kids|Transportation',
5432             11165 => 'Books|Young Adult',
5433             11166 => 'Books|Young Adult|Animals',
5434             11167 => 'Books|Young Adult|Biography',
5435             11168 => 'Books|Young Adult|Careers & Occupations',
5436             11169 => 'Books|Young Adult|Computers & Technology',
5437             11170 => 'Books|Young Adult|Cooking & Food',
5438             11171 => 'Books|Young Adult|Arts & Entertainment',
5439             11172 => 'Books|Young Adult|Arts & Entertainment|Art',
5440             11173 => 'Books|Young Adult|Arts & Entertainment|Crafts',
5441             11174 => 'Books|Young Adult|Arts & Entertainment|Music',
5442             11175 => 'Books|Young Adult|Arts & Entertainment|Performing Arts',
5443             11176 => 'Books|Young Adult|Family',
5444             11177 => 'Books|Young Adult|Fiction',
5445             11178 => 'Books|Young Adult|Fiction|Action & Adventure',
5446             11179 => 'Books|Young Adult|Fiction|Animals',
5447             11180 => 'Books|Young Adult|Fiction|Classics',
5448             11181 => 'Books|Young Adult|Fiction|Comics & Graphic Novels',
5449             11182 => 'Books|Young Adult|Fiction|Culture, Places & People',
5450             11183 => 'Books|Young Adult|Fiction|Dystopian',
5451             11184 => 'Books|Young Adult|Fiction|Family & Relationships',
5452             11185 => 'Books|Young Adult|Fiction|Fantasy',
5453             11186 => 'Books|Young Adult|Fiction|Fairy Tales, Myths & Fables',
5454             11187 => 'Books|Young Adult|Fiction|Favorite Characters',
5455             11188 => 'Books|Young Adult|Fiction|Historical',
5456             11189 => 'Books|Young Adult|Fiction|Holidays & Celebrations',
5457             11190 => 'Books|Young Adult|Fiction|Horror, Monsters & Ghosts',
5458             11191 => 'Books|Young Adult|Fiction|Crime & Mystery',
5459             11192 => 'Books|Young Adult|Fiction|Nature',
5460             11193 => 'Books|Young Adult|Fiction|Religion',
5461             11194 => 'Books|Young Adult|Fiction|Romance',
5462             11195 => 'Books|Young Adult|Fiction|Sci-Fi',
5463             11196 => 'Books|Young Adult|Fiction|Coming of Age',
5464             11197 => 'Books|Young Adult|Fiction|Sports & Recreation',
5465             11198 => 'Books|Young Adult|Fiction|Transportation',
5466             11199 => 'Books|Young Adult|Games & Activities',
5467             11200 => 'Books|Young Adult|General Nonfiction',
5468             11201 => 'Books|Young Adult|Health',
5469             11202 => 'Books|Young Adult|History',
5470             11203 => 'Books|Young Adult|Holidays & Celebrations',
5471             11204 => 'Books|Young Adult|Holidays & Celebrations|Birthdays',
5472             11205 => 'Books|Young Adult|Holidays & Celebrations|Christmas & Advent',
5473             11206 => 'Books|Young Adult|Holidays & Celebrations|Easter & Lent',
5474             11207 => 'Books|Young Adult|Holidays & Celebrations|General',
5475             11208 => 'Books|Young Adult|Holidays & Celebrations|Halloween',
5476             11209 => 'Books|Young Adult|Holidays & Celebrations|Hanukkah',
5477             11210 => 'Books|Young Adult|Holidays & Celebrations|Other',
5478             11211 => 'Books|Young Adult|Holidays & Celebrations|Passover',
5479             11212 => 'Books|Young Adult|Holidays & Celebrations|Patriotic Holidays',
5480             11213 => 'Books|Young Adult|Holidays & Celebrations|Ramadan',
5481             11214 => 'Books|Young Adult|Holidays & Celebrations|Thanksgiving',
5482             11215 => "Books|Young Adult|Holidays & Celebrations|Valentine's Day",
5483             11216 => 'Books|Young Adult|Humor',
5484             11217 => 'Books|Young Adult|Humor|Jokes & Riddles',
5485             11218 => 'Books|Young Adult|Poetry',
5486             11219 => 'Books|Young Adult|Politics & Government',
5487             11220 => 'Books|Young Adult|Reference',
5488             11221 => 'Books|Young Adult|Religion',
5489             11222 => 'Books|Young Adult|Science & Nature',
5490             11223 => 'Books|Young Adult|Coming of Age',
5491             11224 => 'Books|Young Adult|Social Studies',
5492             11225 => 'Books|Young Adult|Sports & Recreation',
5493             11226 => 'Books|Young Adult|Transportation',
5494             11227 => 'Books|Communications & Media',
5495             11228 => 'Books|Military & Warfare',
5496             11229 => 'Books|Romance|Inspirational',
5497             11231 => 'Books|Romance|Holiday',
5498             11232 => 'Books|Romance|Wholesome',
5499             11233 => 'Books|Romance|Military',
5500             11234 => 'Books|Arts & Entertainment|Art History',
5501             11236 => 'Books|Arts & Entertainment|Design',
5502             11243 => 'Books|Business & Personal Finance|Accounting',
5503             11244 => 'Books|Business & Personal Finance|Hospitality',
5504             11245 => 'Books|Business & Personal Finance|Real Estate',
5505             11246 => 'Books|Humor|Jokes & Riddles',
5506             11247 => 'Books|Religion & Spirituality|Comparative Religion',
5507             11255 => 'Books|Cookbooks, Food & Wine|Culinary Arts',
5508             11259 => 'Books|Mysteries & Thrillers|Cozy',
5509             11260 => 'Books|Politics & Current Events|Current Events',
5510             11261 => 'Books|Politics & Current Events|Foreign Policy & International Relations',
5511             11262 => 'Books|Politics & Current Events|Local Government',
5512             11263 => 'Books|Politics & Current Events|National Government',
5513             11264 => 'Books|Politics & Current Events|Political Science',
5514             11265 => 'Books|Politics & Current Events|Public Administration',
5515             11266 => 'Books|Politics & Current Events|World Affairs',
5516             11273 => 'Books|Nonfiction|Family & Relationships|Family & Childcare',
5517             11274 => 'Books|Nonfiction|Family & Relationships|Love & Romance',
5518             11275 => 'Books|Sci-Fi & Fantasy|Fantasy|Urban',
5519             11276 => 'Books|Reference|Foreign Languages|Arabic',
5520             11277 => 'Books|Reference|Foreign Languages|Bilingual Editions',
5521             11278 => 'Books|Reference|Foreign Languages|African Languages',
5522             11279 => 'Books|Reference|Foreign Languages|Ancient Languages',
5523             11280 => 'Books|Reference|Foreign Languages|Chinese',
5524             11281 => 'Books|Reference|Foreign Languages|English',
5525             11282 => 'Books|Reference|Foreign Languages|French',
5526             11283 => 'Books|Reference|Foreign Languages|German',
5527             11284 => 'Books|Reference|Foreign Languages|Hebrew',
5528             11285 => 'Books|Reference|Foreign Languages|Hindi',
5529             11286 => 'Books|Reference|Foreign Languages|Italian',
5530             11287 => 'Books|Reference|Foreign Languages|Japanese',
5531             11288 => 'Books|Reference|Foreign Languages|Korean',
5532             11289 => 'Books|Reference|Foreign Languages|Linguistics',
5533             11290 => 'Books|Reference|Foreign Languages|Other Languages',
5534             11291 => 'Books|Reference|Foreign Languages|Portuguese',
5535             11292 => 'Books|Reference|Foreign Languages|Russian',
5536             11293 => 'Books|Reference|Foreign Languages|Spanish',
5537             11294 => 'Books|Reference|Foreign Languages|Speech Pathology',
5538             11295 => 'Books|Science & Nature|Mathematics|Advanced Mathematics',
5539             11296 => 'Books|Science & Nature|Mathematics|Algebra',
5540             11297 => 'Books|Science & Nature|Mathematics|Arithmetic',
5541             11298 => 'Books|Science & Nature|Mathematics|Calculus',
5542             11299 => 'Books|Science & Nature|Mathematics|Geometry',
5543             11300 => 'Books|Science & Nature|Mathematics|Statistics',
5544             11301 => 'Books|Professional & Technical|Medical|Veterinary',
5545             11302 => 'Books|Professional & Technical|Medical|Neuroscience',
5546             11303 => 'Books|Professional & Technical|Medical|Immunology',
5547             11304 => 'Books|Professional & Technical|Medical|Nursing',
5548             11305 => 'Books|Professional & Technical|Medical|Pharmacology & Toxicology',
5549             11306 => 'Books|Professional & Technical|Medical|Anatomy & Physiology',
5550             11307 => 'Books|Professional & Technical|Medical|Dentistry',
5551             11308 => 'Books|Professional & Technical|Medical|Emergency Medicine',
5552             11309 => 'Books|Professional & Technical|Medical|Genetics',
5553             11310 => 'Books|Professional & Technical|Medical|Psychiatry',
5554             11311 => 'Books|Professional & Technical|Medical|Radiology',
5555             11312 => 'Books|Professional & Technical|Medical|Alternative Medicine',
5556             11317 => 'Books|Nonfiction|Philosophy|Political Philosophy',
5557             11319 => 'Books|Nonfiction|Philosophy|Philosophy of Language',
5558             11320 => 'Books|Nonfiction|Philosophy|Philosophy of Religion',
5559             11327 => 'Books|Nonfiction|Social Science|Sociology',
5560             11329 => 'Books|Professional & Technical|Engineering|Aeronautics',
5561             11330 => 'Books|Professional & Technical|Engineering|Chemical & Petroleum Engineering',
5562             11331 => 'Books|Professional & Technical|Engineering|Civil Engineering',
5563             11332 => 'Books|Professional & Technical|Engineering|Computer Science',
5564             11333 => 'Books|Professional & Technical|Engineering|Electrical Engineering',
5565             11334 => 'Books|Professional & Technical|Engineering|Environmental Engineering',
5566             11335 => 'Books|Professional & Technical|Engineering|Mechanical Engineering',
5567             11336 => 'Books|Professional & Technical|Engineering|Power Resources',
5568             11337 => 'Books|Comics & Graphic Novels|Manga|Boys',
5569             11338 => 'Books|Comics & Graphic Novels|Manga|Men',
5570             11339 => 'Books|Comics & Graphic Novels|Manga|Girls',
5571             11340 => 'Books|Comics & Graphic Novels|Manga|Women',
5572             11341 => 'Books|Comics & Graphic Novels|Manga|Other',
5573             11342 => 'Books|Comics & Graphic Novels|Manga|Yaoi',
5574             11343 => 'Books|Comics & Graphic Novels|Manga|Comic Essays',
5575             12001 => 'Mac App Store|Business',
5576             12002 => 'Mac App Store|Developer Tools',
5577             12003 => 'Mac App Store|Education',
5578             12004 => 'Mac App Store|Entertainment',
5579             12005 => 'Mac App Store|Finance',
5580             12006 => 'Mac App Store|Games',
5581             12007 => 'Mac App Store|Health & Fitness',
5582             12008 => 'Mac App Store|Lifestyle',
5583             12010 => 'Mac App Store|Medical',
5584             12011 => 'Mac App Store|Music',
5585             12012 => 'Mac App Store|News',
5586             12013 => 'Mac App Store|Photography',
5587             12014 => 'Mac App Store|Productivity',
5588             12015 => 'Mac App Store|Reference',
5589             12016 => 'Mac App Store|Social Networking',
5590             12017 => 'Mac App Store|Sports',
5591             12018 => 'Mac App Store|Travel',
5592             12019 => 'Mac App Store|Utilities',
5593             12020 => 'Mac App Store|Video',
5594             12021 => 'Mac App Store|Weather',
5595             12022 => 'Mac App Store|Graphics & Design',
5596             12201 => 'Mac App Store|Games|Action',
5597             12202 => 'Mac App Store|Games|Adventure',
5598             12203 => 'Mac App Store|Games|Casual',
5599             12204 => 'Mac App Store|Games|Board',
5600             12205 => 'Mac App Store|Games|Card',
5601             12206 => 'Mac App Store|Games|Casino',
5602             12207 => 'Mac App Store|Games|Dice',
5603             12208 => 'Mac App Store|Games|Educational',
5604             12209 => 'Mac App Store|Games|Family',
5605             12210 => 'Mac App Store|Games|Kids',
5606             12211 => 'Mac App Store|Games|Music',
5607             12212 => 'Mac App Store|Games|Puzzle',
5608             12213 => 'Mac App Store|Games|Racing',
5609             12214 => 'Mac App Store|Games|Role Playing',
5610             12215 => 'Mac App Store|Games|Simulation',
5611             12216 => 'Mac App Store|Games|Sports',
5612             12217 => 'Mac App Store|Games|Strategy',
5613             12218 => 'Mac App Store|Games|Trivia',
5614             12219 => 'Mac App Store|Games|Word',
5615             13001 => 'App Store|Magazines & Newspapers|News & Politics',
5616             13002 => 'App Store|Magazines & Newspapers|Fashion & Style',
5617             13003 => 'App Store|Magazines & Newspapers|Home & Garden',
5618             13004 => 'App Store|Magazines & Newspapers|Outdoors & Nature',
5619             13005 => 'App Store|Magazines & Newspapers|Sports & Leisure',
5620             13006 => 'App Store|Magazines & Newspapers|Automotive',
5621             13007 => 'App Store|Magazines & Newspapers|Arts & Photography',
5622             13008 => 'App Store|Magazines & Newspapers|Brides & Weddings',
5623             13009 => 'App Store|Magazines & Newspapers|Business & Investing',
5624             13010 => "App Store|Magazines & Newspapers|Children's Magazines",
5625             13011 => 'App Store|Magazines & Newspapers|Computers & Internet',
5626             13012 => 'App Store|Magazines & Newspapers|Cooking, Food & Drink',
5627             13013 => 'App Store|Magazines & Newspapers|Crafts & Hobbies',
5628             13014 => 'App Store|Magazines & Newspapers|Electronics & Audio',
5629             13015 => 'App Store|Magazines & Newspapers|Entertainment',
5630             13017 => 'App Store|Magazines & Newspapers|Health, Mind & Body',
5631             13018 => 'App Store|Magazines & Newspapers|History',
5632             13019 => 'App Store|Magazines & Newspapers|Literary Magazines & Journals',
5633             13020 => "App Store|Magazines & Newspapers|Men's Interest",
5634             13021 => 'App Store|Magazines & Newspapers|Movies & Music',
5635             13023 => 'App Store|Magazines & Newspapers|Parenting & Family',
5636             13024 => 'App Store|Magazines & Newspapers|Pets',
5637             13025 => 'App Store|Magazines & Newspapers|Professional & Trade',
5638             13026 => 'App Store|Magazines & Newspapers|Regional News',
5639             13027 => 'App Store|Magazines & Newspapers|Science',
5640             13028 => 'App Store|Magazines & Newspapers|Teens',
5641             13029 => 'App Store|Magazines & Newspapers|Travel & Regional',
5642             13030 => "App Store|Magazines & Newspapers|Women's Interest",
5643             15000 => 'Textbooks|Arts & Entertainment',
5644             15001 => 'Textbooks|Arts & Entertainment|Art & Architecture',
5645             15002 => 'Textbooks|Arts & Entertainment|Art & Architecture|Urban Planning',
5646             15003 => 'Textbooks|Arts & Entertainment|Art History',
5647             15004 => 'Textbooks|Arts & Entertainment|Dance',
5648             15005 => 'Textbooks|Arts & Entertainment|Design',
5649             15006 => 'Textbooks|Arts & Entertainment|Fashion',
5650             15007 => 'Textbooks|Arts & Entertainment|Film',
5651             15008 => 'Textbooks|Arts & Entertainment|Games',
5652             15009 => 'Textbooks|Arts & Entertainment|Interior Design',
5653             15010 => 'Textbooks|Arts & Entertainment|Media Arts',
5654             15011 => 'Textbooks|Arts & Entertainment|Music',
5655             15012 => 'Textbooks|Arts & Entertainment|Performing Arts',
5656             15013 => 'Textbooks|Arts & Entertainment|Photography',
5657             15014 => 'Textbooks|Arts & Entertainment|Theater',
5658             15015 => 'Textbooks|Arts & Entertainment|TV',
5659             15016 => 'Textbooks|Arts & Entertainment|Visual Arts',
5660             15017 => 'Textbooks|Biographies & Memoirs',
5661             15018 => 'Textbooks|Business & Personal Finance',
5662             15019 => 'Textbooks|Business & Personal Finance|Accounting',
5663             15020 => 'Textbooks|Business & Personal Finance|Careers',
5664             15021 => 'Textbooks|Business & Personal Finance|Economics',
5665             15022 => 'Textbooks|Business & Personal Finance|Finance',
5666             15023 => 'Textbooks|Business & Personal Finance|Hospitality',
5667             15024 => 'Textbooks|Business & Personal Finance|Industries & Professions',
5668             15025 => 'Textbooks|Business & Personal Finance|Investing',
5669             15026 => 'Textbooks|Business & Personal Finance|Management & Leadership',
5670             15027 => 'Textbooks|Business & Personal Finance|Marketing & Sales',
5671             15028 => 'Textbooks|Business & Personal Finance|Personal Finance',
5672             15029 => 'Textbooks|Business & Personal Finance|Real Estate',
5673             15030 => 'Textbooks|Business & Personal Finance|Reference',
5674             15031 => 'Textbooks|Business & Personal Finance|Small Business & Entrepreneurship',
5675             15032 => 'Textbooks|Children & Teens',
5676             15033 => 'Textbooks|Children & Teens|Fiction',
5677             15034 => 'Textbooks|Children & Teens|Nonfiction',
5678             15035 => 'Textbooks|Comics & Graphic Novels',
5679             15036 => 'Textbooks|Comics & Graphic Novels|Graphic Novels',
5680             15037 => 'Textbooks|Comics & Graphic Novels|Manga',
5681             15038 => 'Textbooks|Communications & Media',
5682             15039 => 'Textbooks|Communications & Media|Broadcasting',
5683             15040 => 'Textbooks|Communications & Media|Digital Media',
5684             15041 => 'Textbooks|Communications & Media|Journalism',
5685             15042 => 'Textbooks|Communications & Media|Photojournalism',
5686             15043 => 'Textbooks|Communications & Media|Print',
5687             15044 => 'Textbooks|Communications & Media|Speech',
5688             15045 => 'Textbooks|Communications & Media|Writing',
5689             15046 => 'Textbooks|Computers & Internet',
5690             15047 => 'Textbooks|Computers & Internet|Computers',
5691             15048 => 'Textbooks|Computers & Internet|Databases',
5692             15049 => 'Textbooks|Computers & Internet|Digital Media',
5693             15050 => 'Textbooks|Computers & Internet|Internet',
5694             15051 => 'Textbooks|Computers & Internet|Network',
5695             15052 => 'Textbooks|Computers & Internet|Operating Systems',
5696             15053 => 'Textbooks|Computers & Internet|Programming',
5697             15054 => 'Textbooks|Computers & Internet|Software',
5698             15055 => 'Textbooks|Computers & Internet|System Administration',
5699             15056 => 'Textbooks|Cookbooks, Food & Wine',
5700             15057 => 'Textbooks|Cookbooks, Food & Wine|Beverages',
5701             15058 => 'Textbooks|Cookbooks, Food & Wine|Courses & Dishes',
5702             15059 => 'Textbooks|Cookbooks, Food & Wine|Culinary Arts',
5703             15060 => 'Textbooks|Cookbooks, Food & Wine|Methods',
5704             15061 => 'Textbooks|Cookbooks, Food & Wine|Reference',
5705             15062 => 'Textbooks|Cookbooks, Food & Wine|Regional & Ethnic',
5706             15063 => 'Textbooks|Cookbooks, Food & Wine|Special Diet',
5707             15064 => 'Textbooks|Cookbooks, Food & Wine|Special Occasions',
5708             15065 => 'Textbooks|Cookbooks, Food & Wine|Specific Ingredients',
5709             15066 => 'Textbooks|Engineering',
5710             15067 => 'Textbooks|Engineering|Aeronautics',
5711             15068 => 'Textbooks|Engineering|Chemical & Petroleum Engineering',
5712             15069 => 'Textbooks|Engineering|Civil Engineering',
5713             15070 => 'Textbooks|Engineering|Computer Science',
5714             15071 => 'Textbooks|Engineering|Electrical Engineering',
5715             15072 => 'Textbooks|Engineering|Environmental Engineering',
5716             15073 => 'Textbooks|Engineering|Mechanical Engineering',
5717             15074 => 'Textbooks|Engineering|Power Resources',
5718             15075 => 'Textbooks|Fiction & Literature',
5719             15076 => 'Textbooks|Fiction & Literature|Latino',
5720             15077 => 'Textbooks|Fiction & Literature|Action & Adventure',
5721             15078 => 'Textbooks|Fiction & Literature|African American',
5722             15079 => 'Textbooks|Fiction & Literature|Anthologies',
5723             15080 => 'Textbooks|Fiction & Literature|Classics',
5724             15081 => 'Textbooks|Fiction & Literature|Comparative Literature',
5725             15082 => 'Textbooks|Fiction & Literature|Erotica',
5726             15083 => 'Textbooks|Fiction & Literature|Gay',
5727             15084 => 'Textbooks|Fiction & Literature|Ghost',
5728             15085 => 'Textbooks|Fiction & Literature|Historical',
5729             15086 => 'Textbooks|Fiction & Literature|Horror',
5730             15087 => 'Textbooks|Fiction & Literature|Literary',
5731             15088 => 'Textbooks|Fiction & Literature|Literary Criticism',
5732             15089 => 'Textbooks|Fiction & Literature|Poetry',
5733             15090 => 'Textbooks|Fiction & Literature|Religious',
5734             15091 => 'Textbooks|Fiction & Literature|Short Stories',
5735             15092 => 'Textbooks|Health, Mind & Body',
5736             15093 => 'Textbooks|Health, Mind & Body|Fitness',
5737             15094 => 'Textbooks|Health, Mind & Body|Self-Improvement',
5738             15095 => 'Textbooks|History',
5739             15096 => 'Textbooks|History|Africa',
5740             15097 => 'Textbooks|History|Americas',
5741             15098 => 'Textbooks|History|Americas|Canada',
5742             15099 => 'Textbooks|History|Americas|Latin America',
5743             15100 => 'Textbooks|History|Americas|United States',
5744             15101 => 'Textbooks|History|Ancient',
5745             15102 => 'Textbooks|History|Asia',
5746             15103 => 'Textbooks|History|Australia & Oceania',
5747             15104 => 'Textbooks|History|Europe',
5748             15105 => 'Textbooks|History|Middle East',
5749             15106 => 'Textbooks|History|Military',
5750             15107 => 'Textbooks|History|World',
5751             15108 => 'Textbooks|Humor',
5752             15109 => 'Textbooks|Language Studies',
5753             15110 => 'Textbooks|Language Studies|African Languages',
5754             15111 => 'Textbooks|Language Studies|Ancient Languages',
5755             15112 => 'Textbooks|Language Studies|Arabic',
5756             15113 => 'Textbooks|Language Studies|Bilingual Editions',
5757             15114 => 'Textbooks|Language Studies|Chinese',
5758             15115 => 'Textbooks|Language Studies|English',
5759             15116 => 'Textbooks|Language Studies|French',
5760             15117 => 'Textbooks|Language Studies|German',
5761             15118 => 'Textbooks|Language Studies|Hebrew',
5762             15119 => 'Textbooks|Language Studies|Hindi',
5763             15120 => 'Textbooks|Language Studies|Indigenous Languages',
5764             15121 => 'Textbooks|Language Studies|Italian',
5765             15122 => 'Textbooks|Language Studies|Japanese',
5766             15123 => 'Textbooks|Language Studies|Korean',
5767             15124 => 'Textbooks|Language Studies|Linguistics',
5768             15125 => 'Textbooks|Language Studies|Other Language',
5769             15126 => 'Textbooks|Language Studies|Portuguese',
5770             15127 => 'Textbooks|Language Studies|Russian',
5771             15128 => 'Textbooks|Language Studies|Spanish',
5772             15129 => 'Textbooks|Language Studies|Speech Pathology',
5773             15130 => 'Textbooks|Lifestyle & Home',
5774             15131 => 'Textbooks|Lifestyle & Home|Antiques & Collectibles',
5775             15132 => 'Textbooks|Lifestyle & Home|Crafts & Hobbies',
5776             15133 => 'Textbooks|Lifestyle & Home|Gardening',
5777             15134 => 'Textbooks|Lifestyle & Home|Pets',
5778             15135 => 'Textbooks|Mathematics',
5779             15136 => 'Textbooks|Mathematics|Advanced Mathematics',
5780             15137 => 'Textbooks|Mathematics|Algebra',
5781             15138 => 'Textbooks|Mathematics|Arithmetic',
5782             15139 => 'Textbooks|Mathematics|Calculus',
5783             15140 => 'Textbooks|Mathematics|Geometry',
5784             15141 => 'Textbooks|Mathematics|Statistics',
5785             15142 => 'Textbooks|Medicine',
5786             15143 => 'Textbooks|Medicine|Anatomy & Physiology',
5787             15144 => 'Textbooks|Medicine|Dentistry',
5788             15145 => 'Textbooks|Medicine|Emergency Medicine',
5789             15146 => 'Textbooks|Medicine|Genetics',
5790             15147 => 'Textbooks|Medicine|Immunology',
5791             15148 => 'Textbooks|Medicine|Neuroscience',
5792             15149 => 'Textbooks|Medicine|Nursing',
5793             15150 => 'Textbooks|Medicine|Pharmacology & Toxicology',
5794             15151 => 'Textbooks|Medicine|Psychiatry',
5795             15152 => 'Textbooks|Medicine|Psychology',
5796             15153 => 'Textbooks|Medicine|Radiology',
5797             15154 => 'Textbooks|Medicine|Veterinary',
5798             15155 => 'Textbooks|Mysteries & Thrillers',
5799             15156 => 'Textbooks|Mysteries & Thrillers|British Detectives',
5800             15157 => 'Textbooks|Mysteries & Thrillers|Hard-Boiled',
5801             15158 => 'Textbooks|Mysteries & Thrillers|Historical',
5802             15159 => 'Textbooks|Mysteries & Thrillers|Police Procedural',
5803             15160 => 'Textbooks|Mysteries & Thrillers|Short Stories',
5804             15161 => 'Textbooks|Mysteries & Thrillers|Women Sleuths',
5805             15162 => 'Textbooks|Nonfiction',
5806             15163 => 'Textbooks|Nonfiction|Family & Relationships',
5807             15164 => 'Textbooks|Nonfiction|Transportation',
5808             15165 => 'Textbooks|Nonfiction|True Crime',
5809             15166 => 'Textbooks|Parenting',
5810             15167 => 'Textbooks|Philosophy',
5811             15168 => 'Textbooks|Philosophy|Aesthetics',
5812             15169 => 'Textbooks|Philosophy|Epistemology',
5813             15170 => 'Textbooks|Philosophy|Ethics',
5814             15171 => 'Textbooks|Philosophy|Philosophy of Language',
5815             15172 => 'Textbooks|Philosophy|Logic',
5816             15173 => 'Textbooks|Philosophy|Metaphysics',
5817             15174 => 'Textbooks|Philosophy|Political Philosophy',
5818             15175 => 'Textbooks|Philosophy|Philosophy of Religion',
5819             15176 => 'Textbooks|Politics & Current Events',
5820             15177 => 'Textbooks|Politics & Current Events|Current Events',
5821             15178 => 'Textbooks|Politics & Current Events|Foreign Policy & International Relations',
5822             15179 => 'Textbooks|Politics & Current Events|Local Governments',
5823             15180 => 'Textbooks|Politics & Current Events|National Governments',
5824             15181 => 'Textbooks|Politics & Current Events|Political Science',
5825             15182 => 'Textbooks|Politics & Current Events|Public Administration',
5826             15183 => 'Textbooks|Politics & Current Events|World Affairs',
5827             15184 => 'Textbooks|Professional & Technical',
5828             15185 => 'Textbooks|Professional & Technical|Design',
5829             15186 => 'Textbooks|Professional & Technical|Language Arts & Disciplines',
5830             15187 => 'Textbooks|Professional & Technical|Engineering',
5831             15188 => 'Textbooks|Professional & Technical|Law',
5832             15189 => 'Textbooks|Professional & Technical|Medical',
5833             15190 => 'Textbooks|Reference',
5834             15191 => 'Textbooks|Reference|Almanacs & Yearbooks',
5835             15192 => 'Textbooks|Reference|Atlases & Maps',
5836             15193 => 'Textbooks|Reference|Catalogs & Directories',
5837             15194 => 'Textbooks|Reference|Consumer Guides',
5838             15195 => 'Textbooks|Reference|Dictionaries & Thesauruses',
5839             15196 => 'Textbooks|Reference|Encyclopedias',
5840             15197 => 'Textbooks|Reference|Etiquette',
5841             15198 => 'Textbooks|Reference|Quotations',
5842             15199 => 'Textbooks|Reference|Study Aids',
5843             15200 => 'Textbooks|Reference|Words & Language',
5844             15201 => 'Textbooks|Reference|Writing',
5845             15202 => 'Textbooks|Religion & Spirituality',
5846             15203 => 'Textbooks|Religion & Spirituality|Bible Studies',
5847             15204 => 'Textbooks|Religion & Spirituality|Bibles',
5848             15205 => 'Textbooks|Religion & Spirituality|Buddhism',
5849             15206 => 'Textbooks|Religion & Spirituality|Christianity',
5850             15207 => 'Textbooks|Religion & Spirituality|Comparative Religion',
5851             15208 => 'Textbooks|Religion & Spirituality|Hinduism',
5852             15209 => 'Textbooks|Religion & Spirituality|Islam',
5853             15210 => 'Textbooks|Religion & Spirituality|Judaism',
5854             15211 => 'Textbooks|Religion & Spirituality|Spirituality',
5855             15212 => 'Textbooks|Romance',
5856             15213 => 'Textbooks|Romance|Contemporary',
5857             15214 => 'Textbooks|Romance|Erotic Romance',
5858             15215 => 'Textbooks|Romance|Paranormal',
5859             15216 => 'Textbooks|Romance|Historical',
5860             15217 => 'Textbooks|Romance|Short Stories',
5861             15218 => 'Textbooks|Romance|Suspense',
5862             15219 => 'Textbooks|Romance|Western',
5863             15220 => 'Textbooks|Sci-Fi & Fantasy',
5864             15221 => 'Textbooks|Sci-Fi & Fantasy|Fantasy',
5865             15222 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Contemporary',
5866             15223 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Epic',
5867             15224 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Historical',
5868             15225 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Paranormal',
5869             15226 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Short Stories',
5870             15227 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction',
5871             15228 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature',
5872             15229 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature|Adventure',
5873             15230 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature|High Tech',
5874             15231 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature|Short Stories',
5875             15232 => 'Textbooks|Science & Nature',
5876             15233 => 'Textbooks|Science & Nature|Agriculture',
5877             15234 => 'Textbooks|Science & Nature|Astronomy',
5878             15235 => 'Textbooks|Science & Nature|Atmosphere',
5879             15236 => 'Textbooks|Science & Nature|Biology',
5880             15237 => 'Textbooks|Science & Nature|Chemistry',
5881             15238 => 'Textbooks|Science & Nature|Earth Sciences',
5882             15239 => 'Textbooks|Science & Nature|Ecology',
5883             15240 => 'Textbooks|Science & Nature|Environment',
5884             15241 => 'Textbooks|Science & Nature|Essays',
5885             15242 => 'Textbooks|Science & Nature|Geography',
5886             15243 => 'Textbooks|Science & Nature|Geology',
5887             15244 => 'Textbooks|Science & Nature|History',
5888             15245 => 'Textbooks|Science & Nature|Life Sciences',
5889             15246 => 'Textbooks|Science & Nature|Nature',
5890             15247 => 'Textbooks|Science & Nature|Physics',
5891             15248 => 'Textbooks|Science & Nature|Reference',
5892             15249 => 'Textbooks|Social Science',
5893             15250 => 'Textbooks|Social Science|Anthropology',
5894             15251 => 'Textbooks|Social Science|Archaeology',
5895             15252 => 'Textbooks|Social Science|Civics',
5896             15253 => 'Textbooks|Social Science|Government',
5897             15254 => 'Textbooks|Social Science|Social Studies',
5898             15255 => 'Textbooks|Social Science|Social Welfare',
5899             15256 => 'Textbooks|Social Science|Society',
5900             15257 => 'Textbooks|Social Science|Society|African Studies',
5901             15258 => 'Textbooks|Social Science|Society|American Studies',
5902             15259 => 'Textbooks|Social Science|Society|Asia Pacific Studies',
5903             15260 => 'Textbooks|Social Science|Society|Cross-Cultural Studies',
5904             15261 => 'Textbooks|Social Science|Society|European Studies',
5905             15262 => 'Textbooks|Social Science|Society|Immigration & Emigration',
5906             15263 => 'Textbooks|Social Science|Society|Indigenous Studies',
5907             15264 => 'Textbooks|Social Science|Society|Latin & Caribbean Studies',
5908             15265 => 'Textbooks|Social Science|Society|Middle Eastern Studies',
5909             15266 => 'Textbooks|Social Science|Society|Race & Ethnicity Studies',
5910             15267 => 'Textbooks|Social Science|Society|Sexuality Studies',
5911             15268 => "Textbooks|Social Science|Society|Women's Studies",
5912             15269 => 'Textbooks|Social Science|Sociology',
5913             15270 => 'Textbooks|Sports & Outdoors',
5914             15271 => 'Textbooks|Sports & Outdoors|Baseball',
5915             15272 => 'Textbooks|Sports & Outdoors|Basketball',
5916             15273 => 'Textbooks|Sports & Outdoors|Coaching',
5917             15274 => 'Textbooks|Sports & Outdoors|Equestrian',
5918             15275 => 'Textbooks|Sports & Outdoors|Extreme Sports',
5919             15276 => 'Textbooks|Sports & Outdoors|Football',
5920             15277 => 'Textbooks|Sports & Outdoors|Golf',
5921             15278 => 'Textbooks|Sports & Outdoors|Hockey',
5922             15279 => 'Textbooks|Sports & Outdoors|Motor Sports',
5923             15280 => 'Textbooks|Sports & Outdoors|Mountaineering',
5924             15281 => 'Textbooks|Sports & Outdoors|Outdoors',
5925             15282 => 'Textbooks|Sports & Outdoors|Racket Sports',
5926             15283 => 'Textbooks|Sports & Outdoors|Reference',
5927             15284 => 'Textbooks|Sports & Outdoors|Soccer',
5928             15285 => 'Textbooks|Sports & Outdoors|Training',
5929             15286 => 'Textbooks|Sports & Outdoors|Water Sports',
5930             15287 => 'Textbooks|Sports & Outdoors|Winter Sports',
5931             15288 => 'Textbooks|Teaching & Learning',
5932             15289 => 'Textbooks|Teaching & Learning|Adult Education',
5933             15290 => 'Textbooks|Teaching & Learning|Curriculum & Teaching',
5934             15291 => 'Textbooks|Teaching & Learning|Educational Leadership',
5935             15292 => 'Textbooks|Teaching & Learning|Educational Technology',
5936             15293 => 'Textbooks|Teaching & Learning|Family & Childcare',
5937             15294 => 'Textbooks|Teaching & Learning|Information & Library Science',
5938             15295 => 'Textbooks|Teaching & Learning|Learning Resources',
5939             15296 => 'Textbooks|Teaching & Learning|Psychology & Research',
5940             15297 => 'Textbooks|Teaching & Learning|Special Education',
5941             15298 => 'Textbooks|Travel & Adventure',
5942             15299 => 'Textbooks|Travel & Adventure|Africa',
5943             15300 => 'Textbooks|Travel & Adventure|Americas',
5944             15301 => 'Textbooks|Travel & Adventure|Americas|Canada',
5945             15302 => 'Textbooks|Travel & Adventure|Americas|Latin America',
5946             15303 => 'Textbooks|Travel & Adventure|Americas|United States',
5947             15304 => 'Textbooks|Travel & Adventure|Asia',
5948             15305 => 'Textbooks|Travel & Adventure|Caribbean',
5949             15306 => 'Textbooks|Travel & Adventure|Essays & Memoirs',
5950             15307 => 'Textbooks|Travel & Adventure|Europe',
5951             15308 => 'Textbooks|Travel & Adventure|Middle East',
5952             15309 => 'Textbooks|Travel & Adventure|Oceania',
5953             15310 => 'Textbooks|Travel & Adventure|Specialty Travel',
5954             15311 => 'Textbooks|Comics & Graphic Novels|Comics',
5955             15312 => 'Textbooks|Reference|Manuals',
5956             16001 => 'App Store|Stickers|Emoji & Expressions',
5957             16003 => 'App Store|Stickers|Animals & Nature',
5958             16005 => 'App Store|Stickers|Art',
5959             16006 => 'App Store|Stickers|Celebrations',
5960             16007 => 'App Store|Stickers|Celebrities',
5961             16008 => 'App Store|Stickers|Comics & Cartoons',
5962             16009 => 'App Store|Stickers|Eating & Drinking',
5963             16010 => 'App Store|Stickers|Gaming',
5964             16014 => 'App Store|Stickers|Movies & TV',
5965             16015 => 'App Store|Stickers|Music',
5966             16017 => 'App Store|Stickers|People',
5967             16019 => 'App Store|Stickers|Places & Objects',
5968             16021 => 'App Store|Stickers|Sports & Activities',
5969             16025 => 'App Store|Stickers|Kids & Family',
5970             16026 => 'App Store|Stickers|Fashion',
5971             100000 => 'Music|Christian & Gospel',
5972             100001 => 'Music|Classical|Art Song',
5973             100002 => 'Music|Classical|Brass & Woodwinds',
5974             100003 => 'Music|Classical|Solo Instrumental',
5975             100004 => 'Music|Classical|Contemporary Era',
5976             100005 => 'Music|Classical|Oratorio',
5977             100006 => 'Music|Classical|Cantata',
5978             100007 => 'Music|Classical|Electronic',
5979             100008 => 'Music|Classical|Sacred',
5980             100009 => 'Music|Classical|Guitar',
5981             100010 => 'Music|Classical|Piano',
5982             100011 => 'Music|Classical|Violin',
5983             100012 => 'Music|Classical|Cello',
5984             100013 => 'Music|Classical|Percussion',
5985             100014 => 'Music|Electronic|Dubstep',
5986             100015 => 'Music|Electronic|Bass',
5987             100016 => 'Music|Hip-Hop/Rap|UK Hip-Hop',
5988             100017 => 'Music|Reggae|Lovers Rock',
5989             100018 => 'Music|Alternative|EMO',
5990             100019 => 'Music|Alternative|Pop Punk',
5991             100020 => 'Music|Alternative|Indie Pop',
5992             100021 => 'Music|New Age|Yoga',
5993             100022 => 'Music|Pop|Tribute',
5994             100023 => 'Music|Pop|Shows',
5995             100024 => 'Music|Cuban',
5996             100025 => 'Music|Cuban|Mambo',
5997             100026 => 'Music|Cuban|Chachacha',
5998             100027 => 'Music|Cuban|Guajira',
5999             100028 => 'Music|Cuban|Son',
6000             100029 => 'Music|Cuban|Bolero',
6001             100030 => 'Music|Cuban|Guaracha',
6002             100031 => 'Music|Cuban|Timba',
6003             100032 => 'Music|Soundtrack|Video Game',
6004             100033 => 'Music|Indian|Regional Indian|Punjabi|Punjabi Pop',
6005             100034 => 'Music|Indian|Regional Indian|Bengali|Rabindra Sangeet',
6006             100035 => 'Music|Indian|Regional Indian|Malayalam',
6007             100036 => 'Music|Indian|Regional Indian|Kannada',
6008             100037 => 'Music|Indian|Regional Indian|Marathi',
6009             100038 => 'Music|Indian|Regional Indian|Gujarati',
6010             100039 => 'Music|Indian|Regional Indian|Assamese',
6011             100040 => 'Music|Indian|Regional Indian|Bhojpuri',
6012             100041 => 'Music|Indian|Regional Indian|Haryanvi',
6013             100042 => 'Music|Indian|Regional Indian|Odia',
6014             100043 => 'Music|Indian|Regional Indian|Rajasthani',
6015             100044 => 'Music|Indian|Regional Indian|Urdu',
6016             100045 => 'Music|Indian|Regional Indian|Punjabi',
6017             100046 => 'Music|Indian|Regional Indian|Bengali',
6018             100047 => 'Music|Indian|Indian Classical|Carnatic Classical',
6019             100048 => 'Music|Indian|Indian Classical|Hindustani Classical',
6020             100049 => 'Music|African|Afro House',
6021             100050 => 'Music|African|Afro Soul',
6022             100051 => 'Music|African|Afrobeats',
6023             100052 => 'Music|African|Benga',
6024             100053 => 'Music|African|Bongo-Flava',
6025             100054 => 'Music|African|Coupe-Decale',
6026             100055 => 'Music|African|Gqom',
6027             100056 => 'Music|African|Highlife',
6028             100057 => 'Music|African|Kuduro',
6029             100058 => 'Music|African|Kizomba',
6030             100059 => 'Music|African|Kwaito',
6031             100060 => 'Music|African|Mbalax',
6032             100061 => 'Music|African|Ndombolo',
6033             100062 => 'Music|African|Shangaan Electro',
6034             100063 => 'Music|African|Soukous',
6035             100064 => 'Music|African|Taarab',
6036             100065 => 'Music|African|Zouglou',
6037             100066 => 'Music|Turkish|Ozgun',
6038             100067 => 'Music|Turkish|Fantezi',
6039             100068 => 'Music|Turkish|Religious',
6040             100069 => 'Music|Pop|Turkish Pop',
6041             100070 => 'Music|Rock|Turkish Rock',
6042             100071 => 'Music|Alternative|Turkish Alternative',
6043             100072 => 'Music|Hip-Hop/Rap|Turkish Hip-Hop/Rap',
6044             100073 => 'Music|African|Maskandi',
6045             100074 => 'Music|Russian|Russian Romance',
6046             100075 => 'Music|Russian|Russian Bard',
6047             100076 => 'Music|Russian|Russian Pop',
6048             100077 => 'Music|Russian|Russian Rock',
6049             100078 => 'Music|Russian|Russian Hip-Hop',
6050             100079 => 'Music|Arabic|Levant',
6051             100080 => 'Music|Arabic|Levant|Dabke',
6052             100081 => 'Music|Arabic|Maghreb Rai',
6053             100082 => 'Music|Arabic|Khaleeji|Khaleeji Jalsat',
6054             100083 => 'Music|Arabic|Khaleeji|Khaleeji Shailat',
6055             100084 => 'Music|Tarab',
6056             100085 => 'Music|Tarab|Iraqi Tarab',
6057             100086 => 'Music|Tarab|Egyptian Tarab',
6058             100087 => 'Music|Tarab|Khaleeji Tarab',
6059             100088 => 'Music|Pop|Levant Pop',
6060             100089 => 'Music|Pop|Iraqi Pop',
6061             100090 => 'Music|Pop|Egyptian Pop',
6062             100091 => 'Music|Pop|Maghreb Pop',
6063             100092 => 'Music|Pop|Khaleeji Pop',
6064             100093 => 'Music|Hip-Hop/Rap|Levant Hip-Hop',
6065             100094 => 'Music|Hip-Hop/Rap|Egyptian Hip-Hop',
6066             100095 => 'Music|Hip-Hop/Rap|Maghreb Hip-Hop',
6067             100096 => 'Music|Hip-Hop/Rap|Khaleeji Hip-Hop',
6068             100097 => 'Music|Alternative|Indie Levant',
6069             100098 => 'Music|Alternative|Indie Egyptian',
6070             100099 => 'Music|Alternative|Indie Maghreb',
6071             100100 => 'Music|Electronic|Levant Electronic',
6072             100101 => "Music|Electronic|Electro-Cha'abi",
6073             100102 => 'Music|Electronic|Maghreb Electronic',
6074             100103 => 'Music|Folk|Iraqi Folk',
6075             100104 => 'Music|Folk|Khaleeji Folk',
6076             100105 => 'Music|Dance|Maghreb Dance',
6077             40000000 => 'iTunes U',
6078             40000001 => 'iTunes U|Business & Economics',
6079             40000002 => 'iTunes U|Business & Economics|Economics',
6080             40000003 => 'iTunes U|Business & Economics|Finance',
6081             40000004 => 'iTunes U|Business & Economics|Hospitality',
6082             40000005 => 'iTunes U|Business & Economics|Management',
6083             40000006 => 'iTunes U|Business & Economics|Marketing',
6084             40000007 => 'iTunes U|Business & Economics|Personal Finance',
6085             40000008 => 'iTunes U|Business & Economics|Real Estate',
6086             40000009 => 'iTunes U|Engineering',
6087             40000010 => 'iTunes U|Engineering|Chemical & Petroleum Engineering',
6088             40000011 => 'iTunes U|Engineering|Civil Engineering',
6089             40000012 => 'iTunes U|Engineering|Computer Science',
6090             40000013 => 'iTunes U|Engineering|Electrical Engineering',
6091             40000014 => 'iTunes U|Engineering|Environmental Engineering',
6092             40000015 => 'iTunes U|Engineering|Mechanical Engineering',
6093             40000016 => 'iTunes U|Music, Art, & Design',
6094             40000017 => 'iTunes U|Music, Art, & Design|Architecture',
6095             40000019 => 'iTunes U|Music, Art, & Design|Art History',
6096             40000020 => 'iTunes U|Music, Art, & Design|Dance',
6097             40000021 => 'iTunes U|Music, Art, & Design|Film',
6098             40000022 => 'iTunes U|Music, Art, & Design|Design',
6099             40000023 => 'iTunes U|Music, Art, & Design|Interior Design',
6100             40000024 => 'iTunes U|Music, Art, & Design|Music',
6101             40000025 => 'iTunes U|Music, Art, & Design|Theater',
6102             40000026 => 'iTunes U|Health & Medicine',
6103             40000027 => 'iTunes U|Health & Medicine|Anatomy & Physiology',
6104             40000028 => 'iTunes U|Health & Medicine|Behavioral Science',
6105             40000029 => 'iTunes U|Health & Medicine|Dentistry',
6106             40000030 => 'iTunes U|Health & Medicine|Diet & Nutrition',
6107             40000031 => 'iTunes U|Health & Medicine|Emergency Medicine',
6108             40000032 => 'iTunes U|Health & Medicine|Genetics',
6109             40000033 => 'iTunes U|Health & Medicine|Gerontology',
6110             40000034 => 'iTunes U|Health & Medicine|Health & Exercise Science',
6111             40000035 => 'iTunes U|Health & Medicine|Immunology',
6112             40000036 => 'iTunes U|Health & Medicine|Neuroscience',
6113             40000037 => 'iTunes U|Health & Medicine|Pharmacology & Toxicology',
6114             40000038 => 'iTunes U|Health & Medicine|Psychiatry',
6115             40000039 => 'iTunes U|Health & Medicine|Global Health',
6116             40000040 => 'iTunes U|Health & Medicine|Radiology',
6117             40000041 => 'iTunes U|History',
6118             40000042 => 'iTunes U|History|Ancient History',
6119             40000043 => 'iTunes U|History|Medieval History',
6120             40000044 => 'iTunes U|History|Military History',
6121             40000045 => 'iTunes U|History|Modern History',
6122             40000046 => 'iTunes U|History|African History',
6123             40000047 => 'iTunes U|History|Asia-Pacific History',
6124             40000048 => 'iTunes U|History|European History',
6125             40000049 => 'iTunes U|History|Middle Eastern History',
6126             40000050 => 'iTunes U|History|North American History',
6127             40000051 => 'iTunes U|History|South American History',
6128             40000053 => 'iTunes U|Communications & Journalism',
6129             40000054 => 'iTunes U|Philosophy',
6130             40000055 => 'iTunes U|Religion & Spirituality',
6131             40000056 => 'iTunes U|Languages',
6132             40000057 => 'iTunes U|Languages|African Languages',
6133             40000058 => 'iTunes U|Languages|Ancient Languages',
6134             40000061 => 'iTunes U|Languages|English',
6135             40000063 => 'iTunes U|Languages|French',
6136             40000064 => 'iTunes U|Languages|German',
6137             40000065 => 'iTunes U|Languages|Italian',
6138             40000066 => 'iTunes U|Languages|Linguistics',
6139             40000068 => 'iTunes U|Languages|Spanish',
6140             40000069 => 'iTunes U|Languages|Speech Pathology',
6141             40000070 => 'iTunes U|Writing & Literature',
6142             40000071 => 'iTunes U|Writing & Literature|Anthologies',
6143             40000072 => 'iTunes U|Writing & Literature|Biography',
6144             40000073 => 'iTunes U|Writing & Literature|Classics',
6145             40000074 => 'iTunes U|Writing & Literature|Literary Criticism',
6146             40000075 => 'iTunes U|Writing & Literature|Fiction',
6147             40000076 => 'iTunes U|Writing & Literature|Poetry',
6148             40000077 => 'iTunes U|Mathematics',
6149             40000078 => 'iTunes U|Mathematics|Advanced Mathematics',
6150             40000079 => 'iTunes U|Mathematics|Algebra',
6151             40000080 => 'iTunes U|Mathematics|Arithmetic',
6152             40000081 => 'iTunes U|Mathematics|Calculus',
6153             40000082 => 'iTunes U|Mathematics|Geometry',
6154             40000083 => 'iTunes U|Mathematics|Statistics',
6155             40000084 => 'iTunes U|Science',
6156             40000085 => 'iTunes U|Science|Agricultural',
6157             40000086 => 'iTunes U|Science|Astronomy',
6158             40000087 => 'iTunes U|Science|Atmosphere',
6159             40000088 => 'iTunes U|Science|Biology',
6160             40000089 => 'iTunes U|Science|Chemistry',
6161             40000090 => 'iTunes U|Science|Ecology',
6162             40000091 => 'iTunes U|Science|Geography',
6163             40000092 => 'iTunes U|Science|Geology',
6164             40000093 => 'iTunes U|Science|Physics',
6165             40000094 => 'iTunes U|Social Science',
6166             40000095 => 'iTunes U|Law & Politics|Law',
6167             40000096 => 'iTunes U|Law & Politics|Political Science',
6168             40000097 => 'iTunes U|Law & Politics|Public Administration',
6169             40000098 => 'iTunes U|Social Science|Psychology',
6170             40000099 => 'iTunes U|Social Science|Social Welfare',
6171             40000100 => 'iTunes U|Social Science|Sociology',
6172             40000101 => 'iTunes U|Society',
6173             40000103 => 'iTunes U|Society|Asia Pacific Studies',
6174             40000104 => 'iTunes U|Society|European Studies',
6175             40000105 => 'iTunes U|Society|Indigenous Studies',
6176             40000106 => 'iTunes U|Society|Latin & Caribbean Studies',
6177             40000107 => 'iTunes U|Society|Middle Eastern Studies',
6178             40000108 => "iTunes U|Society|Women's Studies",
6179             40000109 => 'iTunes U|Teaching & Learning',
6180             40000110 => 'iTunes U|Teaching & Learning|Curriculum & Teaching',
6181             40000111 => 'iTunes U|Teaching & Learning|Educational Leadership',
6182             40000112 => 'iTunes U|Teaching & Learning|Family & Childcare',
6183             40000113 => 'iTunes U|Teaching & Learning|Learning Resources',
6184             40000114 => 'iTunes U|Teaching & Learning|Psychology & Research',
6185             40000115 => 'iTunes U|Teaching & Learning|Special Education',
6186             40000116 => 'iTunes U|Music, Art, & Design|Culinary Arts',
6187             40000117 => 'iTunes U|Music, Art, & Design|Fashion',
6188             40000118 => 'iTunes U|Music, Art, & Design|Media Arts',
6189             40000119 => 'iTunes U|Music, Art, & Design|Photography',
6190             40000120 => 'iTunes U|Music, Art, & Design|Visual Art',
6191             40000121 => 'iTunes U|Business & Economics|Entrepreneurship',
6192             40000122 => 'iTunes U|Communications & Journalism|Broadcasting',
6193             40000123 => 'iTunes U|Communications & Journalism|Digital Media',
6194             40000124 => 'iTunes U|Communications & Journalism|Journalism',
6195             40000125 => 'iTunes U|Communications & Journalism|Photojournalism',
6196             40000126 => 'iTunes U|Communications & Journalism|Print',
6197             40000127 => 'iTunes U|Communications & Journalism|Speech',
6198             40000128 => 'iTunes U|Communications & Journalism|Writing',
6199             40000129 => 'iTunes U|Health & Medicine|Nursing',
6200             40000130 => 'iTunes U|Languages|Arabic',
6201             40000131 => 'iTunes U|Languages|Chinese',
6202             40000132 => 'iTunes U|Languages|Hebrew',
6203             40000133 => 'iTunes U|Languages|Hindi',
6204             40000134 => 'iTunes U|Languages|Indigenous Languages',
6205             40000135 => 'iTunes U|Languages|Japanese',
6206             40000136 => 'iTunes U|Languages|Korean',
6207             40000137 => 'iTunes U|Languages|Other Languages',
6208             40000138 => 'iTunes U|Languages|Portuguese',
6209             40000139 => 'iTunes U|Languages|Russian',
6210             40000140 => 'iTunes U|Law & Politics',
6211             40000141 => 'iTunes U|Law & Politics|Foreign Policy & International Relations',
6212             40000142 => 'iTunes U|Law & Politics|Local Governments',
6213             40000143 => 'iTunes U|Law & Politics|National Governments',
6214             40000144 => 'iTunes U|Law & Politics|World Affairs',
6215             40000145 => 'iTunes U|Writing & Literature|Comparative Literature',
6216             40000146 => 'iTunes U|Philosophy|Aesthetics',
6217             40000147 => 'iTunes U|Philosophy|Epistemology',
6218             40000148 => 'iTunes U|Philosophy|Ethics',
6219             40000149 => 'iTunes U|Philosophy|Metaphysics',
6220             40000150 => 'iTunes U|Philosophy|Political Philosophy',
6221             40000151 => 'iTunes U|Philosophy|Logic',
6222             40000152 => 'iTunes U|Philosophy|Philosophy of Language',
6223             40000153 => 'iTunes U|Philosophy|Philosophy of Religion',
6224             40000154 => 'iTunes U|Social Science|Archaeology',
6225             40000155 => 'iTunes U|Social Science|Anthropology',
6226             40000156 => 'iTunes U|Religion & Spirituality|Buddhism',
6227             40000157 => 'iTunes U|Religion & Spirituality|Christianity',
6228             40000158 => 'iTunes U|Religion & Spirituality|Comparative Religion',
6229             40000159 => 'iTunes U|Religion & Spirituality|Hinduism',
6230             40000160 => 'iTunes U|Religion & Spirituality|Islam',
6231             40000161 => 'iTunes U|Religion & Spirituality|Judaism',
6232             40000162 => 'iTunes U|Religion & Spirituality|Other Religions',
6233             40000163 => 'iTunes U|Religion & Spirituality|Spirituality',
6234             40000164 => 'iTunes U|Science|Environment',
6235             40000165 => 'iTunes U|Society|African Studies',
6236             40000166 => 'iTunes U|Society|American Studies',
6237             40000167 => 'iTunes U|Society|Cross-cultural Studies',
6238             40000168 => 'iTunes U|Society|Immigration & Emigration',
6239             40000169 => 'iTunes U|Society|Race & Ethnicity Studies',
6240             40000170 => 'iTunes U|Society|Sexuality Studies',
6241             40000171 => 'iTunes U|Teaching & Learning|Educational Technology',
6242             40000172 => 'iTunes U|Teaching & Learning|Information/Library Science',
6243             40000173 => 'iTunes U|Languages|Dutch',
6244             40000174 => 'iTunes U|Languages|Luxembourgish',
6245             40000175 => 'iTunes U|Languages|Swedish',
6246             40000176 => 'iTunes U|Languages|Norwegian',
6247             40000177 => 'iTunes U|Languages|Finnish',
6248             40000178 => 'iTunes U|Languages|Danish',
6249             40000179 => 'iTunes U|Languages|Polish',
6250             40000180 => 'iTunes U|Languages|Turkish',
6251             40000181 => 'iTunes U|Languages|Flemish',
6252             50000024 => 'Audiobooks',
6253             50000040 => 'Audiobooks|Fiction',
6254             50000041 => 'Audiobooks|Arts & Entertainment',
6255             50000042 => 'Audiobooks|Biographies & Memoirs',
6256             50000043 => 'Audiobooks|Business & Personal Finance',
6257             50000044 => 'Audiobooks|Kids & Young Adults',
6258             50000045 => 'Audiobooks|Classics',
6259             50000046 => 'Audiobooks|Comedy',
6260             50000047 => 'Audiobooks|Drama & Poetry',
6261             50000048 => 'Audiobooks|Speakers & Storytellers',
6262             50000049 => 'Audiobooks|History',
6263             50000050 => 'Audiobooks|Languages',
6264             50000051 => 'Audiobooks|Mysteries & Thrillers',
6265             50000052 => 'Audiobooks|Nonfiction',
6266             50000053 => 'Audiobooks|Religion & Spirituality',
6267             50000054 => 'Audiobooks|Science & Nature',
6268             50000055 => 'Audiobooks|Sci Fi & Fantasy',
6269             50000056 => 'Audiobooks|Self-Development',
6270             50000057 => 'Audiobooks|Sports & Outdoors',
6271             50000058 => 'Audiobooks|Technology',
6272             50000059 => 'Audiobooks|Travel & Adventure',
6273             50000061 => 'Music|Spoken Word',
6274             50000063 => 'Music|Disney',
6275             50000064 => 'Music|French Pop',
6276             50000066 => 'Music|German Pop',
6277             50000068 => 'Music|German Folk',
6278             50000069 => 'Audiobooks|Romance',
6279             50000070 => 'Audiobooks|Audiobooks Latino',
6280             50000071 => 'Books|Comics & Graphic Novels|Manga|Action',
6281             50000072 => 'Books|Comics & Graphic Novels|Manga|Comedy',
6282             50000073 => 'Books|Comics & Graphic Novels|Manga|Erotica',
6283             50000074 => 'Books|Comics & Graphic Novels|Manga|Fantasy',
6284             50000075 => 'Books|Comics & Graphic Novels|Manga|Four Cell Manga',
6285             50000076 => 'Books|Comics & Graphic Novels|Manga|Gay & Lesbian',
6286             50000077 => 'Books|Comics & Graphic Novels|Manga|Hard-Boiled',
6287             50000078 => 'Books|Comics & Graphic Novels|Manga|Heroes',
6288             50000079 => 'Books|Comics & Graphic Novels|Manga|Historical Fiction',
6289             50000080 => 'Books|Comics & Graphic Novels|Manga|Mecha',
6290             50000081 => 'Books|Comics & Graphic Novels|Manga|Mystery',
6291             50000082 => 'Books|Comics & Graphic Novels|Manga|Nonfiction',
6292             50000083 => 'Books|Comics & Graphic Novels|Manga|Religious',
6293             50000084 => 'Books|Comics & Graphic Novels|Manga|Romance',
6294             50000085 => 'Books|Comics & Graphic Novels|Manga|Romantic Comedy',
6295             50000086 => 'Books|Comics & Graphic Novels|Manga|Science Fiction',
6296             50000087 => 'Books|Comics & Graphic Novels|Manga|Sports',
6297             50000088 => 'Books|Fiction & Literature|Light Novels',
6298             50000089 => 'Books|Comics & Graphic Novels|Manga|Horror',
6299             50000090 => 'Books|Comics & Graphic Novels|Comics',
6300             50000091 => 'Books|Romance|Multicultural',
6301             50000092 => 'Audiobooks|Erotica',
6302             50000093 => 'Audiobooks|Light Novels',
6303             },
6304             },
6305             grup => { Name => 'Grouping', Avoid => 1 }, #10
6306             hdvd => { #10
6307             Name => 'HDVideo',
6308             Format => 'int8u', #24
6309             Writable => 'int8s', #27
6310             PrintConv => { 0 => 'No', 1 => 'Yes' },
6311             },
6312             keyw => 'Keyword', #7
6313             ldes => 'LongDescription', #10
6314             pcst => { #7
6315             Name => 'Podcast',
6316             Format => 'int8u', #23
6317             Writable => 'int8s', #27
6318             PrintConv => { 0 => 'No', 1 => 'Yes' },
6319             },
6320             perf => 'Performer',
6321             plID => {
6322             # (ref 10 called this PlayListID or TVSeason)
6323             Name => 'AlbumID', #28
6324             Format => 'int64u',
6325             Writable => 'int32s', #27
6326             },
6327             purd => 'PurchaseDate', #7
6328             purl => 'PodcastURL', #7
6329             rtng => { #10
6330             Name => 'Rating',
6331             Format => 'int8u', #23
6332             Writable => 'int8s', #27
6333             PrintConv => {
6334             0 => 'none',
6335             1 => 'Explicit',
6336             2 => 'Clean',
6337             4 => 'Explicit (old)',
6338             },
6339             },
6340             sfID => { #10
6341             Name => 'AppleStoreCountry',
6342             Format => 'int32u',
6343             Writable => 'int32s', #27
6344             SeparateTable => 1,
6345             PrintConv => { #21
6346             143441 => 'United States', # US
6347             143442 => 'France', # FR
6348             143443 => 'Germany', # DE
6349             143444 => 'United Kingdom', # GB
6350             143445 => 'Austria', # AT
6351             143446 => 'Belgium', # BE
6352             143447 => 'Finland', # FI
6353             143448 => 'Greece', # GR
6354             143449 => 'Ireland', # IE
6355             143450 => 'Italy', # IT
6356             143451 => 'Luxembourg', # LU
6357             143452 => 'Netherlands', # NL
6358             143453 => 'Portugal', # PT
6359             143454 => 'Spain', # ES
6360             143455 => 'Canada', # CA
6361             143456 => 'Sweden', # SE
6362             143457 => 'Norway', # NO
6363             143458 => 'Denmark', # DK
6364             143459 => 'Switzerland', # CH
6365             143460 => 'Australia', # AU
6366             143461 => 'New Zealand', # NZ
6367             143462 => 'Japan', # JP
6368             143463 => 'Hong Kong', # HK
6369             143464 => 'Singapore', # SG
6370             143465 => 'China', # CN
6371             143466 => 'Republic of Korea', # KR
6372             143467 => 'India', # IN
6373             143468 => 'Mexico', # MX
6374             143469 => 'Russia', # RU
6375             143470 => 'Taiwan', # TW
6376             143471 => 'Vietnam', # VN
6377             143472 => 'South Africa', # ZA
6378             143473 => 'Malaysia', # MY
6379             143474 => 'Philippines', # PH
6380             143475 => 'Thailand', # TH
6381             143476 => 'Indonesia', # ID
6382             143477 => 'Pakistan', # PK
6383             143478 => 'Poland', # PL
6384             143479 => 'Saudi Arabia', # SA
6385             143480 => 'Turkey', # TR
6386             143481 => 'United Arab Emirates', # AE
6387             143482 => 'Hungary', # HU
6388             143483 => 'Chile', # CL
6389             143484 => 'Nepal', # NP
6390             143485 => 'Panama', # PA
6391             143486 => 'Sri Lanka', # LK
6392             143487 => 'Romania', # RO
6393             143489 => 'Czech Republic', # CZ
6394             143491 => 'Israel', # IL
6395             143492 => 'Ukraine', # UA
6396             143493 => 'Kuwait', # KW
6397             143494 => 'Croatia', # HR
6398             143495 => 'Costa Rica', # CR
6399             143496 => 'Slovakia', # SK
6400             143497 => 'Lebanon', # LB
6401             143498 => 'Qatar', # QA
6402             143499 => 'Slovenia', # SI
6403             143501 => 'Colombia', # CO
6404             143502 => 'Venezuela', # VE
6405             143503 => 'Brazil', # BR
6406             143504 => 'Guatemala', # GT
6407             143505 => 'Argentina', # AR
6408             143506 => 'El Salvador', # SV
6409             143507 => 'Peru', # PE
6410             143508 => 'Dominican Republic', # DO
6411             143509 => 'Ecuador', # EC
6412             143510 => 'Honduras', # HN
6413             143511 => 'Jamaica', # JM
6414             143512 => 'Nicaragua', # NI
6415             143513 => 'Paraguay', # PY
6416             143514 => 'Uruguay', # UY
6417             143515 => 'Macau', # MO
6418             143516 => 'Egypt', # EG
6419             143517 => 'Kazakhstan', # KZ
6420             143518 => 'Estonia', # EE
6421             143519 => 'Latvia', # LV
6422             143520 => 'Lithuania', # LT
6423             143521 => 'Malta', # MT
6424             143523 => 'Moldova', # MD
6425             143524 => 'Armenia', # AM
6426             143525 => 'Botswana', # BW
6427             143526 => 'Bulgaria', # BG
6428             143528 => 'Jordan', # JO
6429             143529 => 'Kenya', # KE
6430             143530 => 'Macedonia', # MK
6431             143531 => 'Madagascar', # MG
6432             143532 => 'Mali', # ML
6433             143533 => 'Mauritius', # MU
6434             143534 => 'Niger', # NE
6435             143535 => 'Senegal', # SN
6436             143536 => 'Tunisia', # TN
6437             143537 => 'Uganda', # UG
6438             143538 => 'Anguilla', # AI
6439             143539 => 'Bahamas', # BS
6440             143540 => 'Antigua and Barbuda', # AG
6441             143541 => 'Barbados', # BB
6442             143542 => 'Bermuda', # BM
6443             143543 => 'British Virgin Islands', # VG
6444             143544 => 'Cayman Islands', # KY
6445             143545 => 'Dominica', # DM
6446             143546 => 'Grenada', # GD
6447             143547 => 'Montserrat', # MS
6448             143548 => 'St. Kitts and Nevis', # KN
6449             143549 => 'St. Lucia', # LC
6450             143550 => 'St. Vincent and The Grenadines', # VC
6451             143551 => 'Trinidad and Tobago', # TT
6452             143552 => 'Turks and Caicos', # TC
6453             143553 => 'Guyana', # GY
6454             143554 => 'Suriname', # SR
6455             143555 => 'Belize', # BZ
6456             143556 => 'Bolivia', # BO
6457             143557 => 'Cyprus', # CY
6458             143558 => 'Iceland', # IS
6459             143559 => 'Bahrain', # BH
6460             143560 => 'Brunei Darussalam', # BN
6461             143561 => 'Nigeria', # NG
6462             143562 => 'Oman', # OM
6463             143563 => 'Algeria', # DZ
6464             143564 => 'Angola', # AO
6465             143565 => 'Belarus', # BY
6466             143566 => 'Uzbekistan', # UZ
6467             143568 => 'Azerbaijan', # AZ
6468             143571 => 'Yemen', # YE
6469             143572 => 'Tanzania', # TZ
6470             143573 => 'Ghana', # GH
6471             143575 => 'Albania', # AL
6472             143576 => 'Benin', # BJ
6473             143577 => 'Bhutan', # BT
6474             143578 => 'Burkina Faso', # BF
6475             143579 => 'Cambodia', # KH
6476             143580 => 'Cape Verde', # CV
6477             143581 => 'Chad', # TD
6478             143582 => 'Republic of the Congo', # CG
6479             143583 => 'Fiji', # FJ
6480             143584 => 'Gambia', # GM
6481             143585 => 'Guinea-Bissau', # GW
6482             143586 => 'Kyrgyzstan', # KG
6483             143587 => "Lao People's Democratic Republic", # LA
6484             143588 => 'Liberia', # LR
6485             143589 => 'Malawi', # MW
6486             143590 => 'Mauritania', # MR
6487             143591 => 'Federated States of Micronesia', # FM
6488             143592 => 'Mongolia', # MN
6489             143593 => 'Mozambique', # MZ
6490             143594 => 'Namibia', # NA
6491             143595 => 'Palau', # PW
6492             143597 => 'Papua New Guinea', # PG
6493             143598 => 'Sao Tome and Principe', # ST (São Tomé and Príncipe)
6494             143599 => 'Seychelles', # SC
6495             143600 => 'Sierra Leone', # SL
6496             143601 => 'Solomon Islands', # SB
6497             143602 => 'Swaziland', # SZ
6498             143603 => 'Tajikistan', # TJ
6499             143604 => 'Turkmenistan', # TM
6500             143605 => 'Zimbabwe', # ZW
6501             },
6502             },
6503             soaa => 'SortAlbumArtist', #10
6504             soal => 'SortAlbum', #10
6505             soar => 'SortArtist', #10
6506             soco => 'SortComposer', #10
6507             sonm => 'SortName', #10
6508             sosn => 'SortShow', #10
6509             stik => { #10
6510             Name => 'MediaType',
6511             Format => 'int8u', #23
6512             Writable => 'int8s', #27
6513             PrintConvColumns => 2,
6514             PrintConv => { #(http://weblog.xanga.com/gryphondwb/615474010/iphone-ringtones---what-did-itunes-741-really-do.html)
6515             0 => 'Movie (old)', #forum9059 (was Movie)
6516             1 => 'Normal (Music)',
6517             2 => 'Audiobook',
6518             5 => 'Whacked Bookmark',
6519             6 => 'Music Video',
6520             9 => 'Movie', #forum9059 (was Short Film)
6521             10 => 'TV Show',
6522             11 => 'Booklet',
6523             14 => 'Ringtone',
6524             21 => 'Podcast', #15
6525             23 => 'iTunes U', #forum9059
6526             },
6527             },
6528             rate => 'RatingPercent', #PH
6529             titl => { Name => 'Title', Avoid => 1 },
6530             tven => 'TVEpisodeID', #7
6531             tves => { #7/10
6532             Name => 'TVEpisode',
6533             Format => 'int32u',
6534             Writable => 'int32s', #27
6535             },
6536             tvnn => 'TVNetworkName', #7
6537             tvsh => 'TVShow', #10
6538             tvsn => { #7/10
6539             Name => 'TVSeason',
6540             Format => 'int32u',
6541             },
6542             yrrc => 'Year', #(ffmpeg source)
6543             itnu => { #PH (iTunes 10.5)
6544             Name => 'iTunesU',
6545             Format => 'int8u', #27
6546             Writable => 'int8s', #27
6547             Description => 'iTunes U',
6548             PrintConv => { 0 => 'No', 1 => 'Yes' },
6549             },
6550             #https://github.com/communitymedia/mediautilities/blob/master/src/net/sourceforge/jaad/mp4/boxes/BoxTypes.java
6551             gshh => { Name => 'GoogleHostHeader', Format => 'string' },
6552             gspm => { Name => 'GooglePingMessage', Format => 'string' },
6553             gspu => { Name => 'GooglePingURL', Format => 'string' },
6554             gssd => { Name => 'GoogleSourceData', Format => 'string' },
6555             gsst => { Name => 'GoogleStartTime', Format => 'string' },
6556             gstd => {
6557             Name => 'GoogleTrackDuration',
6558             Format => 'string',
6559             ValueConv => '$val / 1000',
6560             ValueConvInv => '$val * 1000',
6561             PrintConv => 'ConvertDuration($val)',
6562             PrintConvInv => q{
6563             my $sign = ($val =~ s/^-//) ? -1 : 1;
6564             my @a = $val =~ /(\d+(?:\.\d+)?)/g;
6565             unshift @a, 0 while @a < 4;
6566             return $sign * (((($a[0] * 24) + $a[1]) * 60 + $a[2]) * 60 + $a[3]);
6567             },
6568             },
6569              
6570             # atoms observed in AAX audiobooks (ref PH)
6571             "\xa9cpy" => { Name => 'Copyright', Avoid => 1, Groups => { 2 => 'Author' } },
6572             "\xa9pub" => 'Publisher',
6573             "\xa9nrt" => 'Narrator',
6574             '@pti' => 'ParentTitle', # (guess -- same as "\xa9nam")
6575             '@PST' => 'ParentShortTitle', # (guess -- same as "\xa9nam")
6576             '@ppi' => 'ParentProductID', # (guess -- same as 'prID')
6577             '@sti' => 'ShortTitle', # (guess -- same as "\xa9nam")
6578             prID => 'ProductID',
6579             rldt => { Name => 'ReleaseDate', Groups => { 2 => 'Time' }},
6580             CDEK => { Name => 'Unknown_CDEK', Unknown => 1 }, # eg: "B004ZMTFEG" - used in URL's ("asin=")
6581             CDET => { Name => 'Unknown_CDET', Unknown => 1 }, # eg: "ADBL"
6582             VERS => 'ProductVersion',
6583             GUID => 'GUID',
6584             AACR => { Name => 'Unknown_AACR', Unknown => 1 }, # eg: "CR!1T1H1QH6WX7T714G2BMFX3E9MC4S"
6585             # ausr - 30 bytes (User Alias?)
6586             "\xa9xyz" => { #PH (written by Google Photos)
6587             Name => 'GPSCoordinates',
6588             Groups => { 2 => 'Location' },
6589             ValueConv => \&ConvertISO6709,
6590             ValueConvInv => \&ConvInvISO6709,
6591             PrintConv => \&PrintGPSCoordinates,
6592             PrintConvInv => \&PrintInvGPSCoordinates,
6593             },
6594             # the following tags written by iTunes 12.5.1.21
6595             # (ref https://www.ventismedia.com/mantis/view.php?id=14963
6596             # https://community.mp3tag.de/t/x-mp4-new-tag-problems/19488)
6597             "\xa9wrk" => 'Work', #PH
6598             "\xa9mvn" => 'MovementName', #PH
6599             "\xa9mvi" => { #PH
6600             Name => 'MovementNumber',
6601             Format => 'int16u', #27
6602             Writable => 'int16s', #27
6603             },
6604             "\xa9mvc" => { #PH
6605             Name => 'MovementCount',
6606             Format => 'int16u', #27
6607             Writable => 'int16s', #27
6608             },
6609             shwm => { #PH
6610             Name => 'ShowMovement',
6611             Format => 'int8u', #27
6612             Writable => 'int8s', #27
6613             PrintConv => { 0 => 'No', 1 => 'Yes' },
6614             },
6615             ownr => 'Owner', #PH (obscure) (ref ChrisAdan private communication)
6616             'xid ' => 'ISRC', #PH
6617             # found in DJI Osmo Action4 video
6618             tnal => { Name => 'ThumbnailImage', Binary => 1, Groups => { 2 => 'Preview' } },
6619             snal => { Name => 'PreviewImage', Binary => 1, Groups => { 2 => 'Preview' } },
6620             );
6621              
6622             # tag decoded from timed face records
6623             %Image::ExifTool::QuickTime::FaceInfo = (
6624             PROCESS_PROC => \&ProcessMOV,
6625             GROUPS => { 2 => 'Video' },
6626             crec => {
6627             Name => 'FaceRec',
6628             SubDirectory => {
6629             TagTable => 'Image::ExifTool::QuickTime::FaceRec',
6630             },
6631             },
6632             );
6633              
6634             # tag decoded from timed face records
6635             %Image::ExifTool::QuickTime::FaceRec = (
6636             PROCESS_PROC => \&ProcessMOV,
6637             GROUPS => { 2 => 'Video' },
6638             cits => {
6639             Name => 'FaceItem',
6640             SubDirectory => {
6641             TagTable => 'Image::ExifTool::QuickTime::Keys',
6642             ProcessProc => \&Process_mebx,
6643             },
6644             },
6645             );
6646              
6647             # item list keys (ref PH)
6648             %Image::ExifTool::QuickTime::Keys = (
6649             PROCESS_PROC => \&ProcessKeys,
6650             WRITE_PROC => \&WriteKeys,
6651             CHECK_PROC => \&CheckQTValue,
6652             VARS => { LONG_TAGS => 9 },
6653             WRITABLE => 1,
6654             # (not PREFERRED when writing)
6655             GROUPS => { 1 => 'Keys' },
6656             WRITE_GROUP => 'Keys',
6657             LANG_INFO => \&GetLangInfo,
6658             NOTES => q{
6659             This directory contains a list of key names which are used to decode tags
6660             written by the "mdta" handler. Also in this table are a few tags found in
6661             timed metadata that are not yet writable by ExifTool. The prefix of
6662             "com.apple.quicktime." has been removed from most TagID's below. These tags
6663             support alternate languages in the same way as the
6664             L tags. Note
6665             that by default,
6666             L and
6667             L tags are
6668             preferred when writing, so to create a tag when a same-named tag exists in
6669             either of these tables, either the "Keys" location must be specified (eg.
6670             C<-Keys:Author=Phil> on the command line), or the PREFERRED level must be
6671             changed via L.
6672             },
6673             version => 'Version',
6674             album => 'Album',
6675             artist => { },
6676             artwork => { },
6677             author => { Name => 'Author', Groups => { 2 => 'Author' } },
6678             comment => { },
6679             copyright => { Name => 'Copyright', Groups => { 2 => 'Author' } },
6680             creationdate=> {
6681             Name => 'CreationDate',
6682             Groups => { 2 => 'Time' },
6683             %iso8601Date,
6684             },
6685             description => { },
6686             director => { },
6687             displayname => { Name => 'DisplayName' },
6688             title => { }, #22
6689             genre => { },
6690             information => { },
6691             keywords => { },
6692             producer => { }, #22
6693             make => { Name => 'Make', Groups => { 2 => 'Camera' } },
6694             model => { Name => 'Model', Groups => { 2 => 'Camera' } },
6695             publisher => { },
6696             software => { },
6697             year => { Groups => { 2 => 'Time' } },
6698             'location.ISO6709' => {
6699             Name => 'GPSCoordinates',
6700             Groups => { 2 => 'Location' },
6701             Notes => q{
6702             Google Photos may ignore this if the coordinates have more than 5 digits
6703             after the decimal
6704             },
6705             ValueConv => \&ConvertISO6709,
6706             ValueConvInv => \&ConvInvISO6709,
6707             PrintConv => \&PrintGPSCoordinates,
6708             PrintConvInv => \&PrintInvGPSCoordinates,
6709             },
6710             'location.name' => { Name => 'LocationName', Groups => { 2 => 'Location' } },
6711             'location.body' => { Name => 'LocationBody', Groups => { 2 => 'Location' } },
6712             'location.note' => { Name => 'LocationNote', Groups => { 2 => 'Location' } },
6713             'location.role' => {
6714             Name => 'LocationRole',
6715             Groups => { 2 => 'Location' },
6716             PrintConv => {
6717             0 => 'Shooting Location',
6718             1 => 'Real Location',
6719             2 => 'Fictional Location',
6720             },
6721             },
6722             'location.date' => {
6723             Name => 'LocationDate',
6724             Groups => { 2 => 'Time' },
6725             %iso8601Date,
6726             },
6727             'location.accuracy.horizontal' => { Name => 'LocationAccuracyHorizontal' },
6728             'live-photo.auto' => { Name => 'LivePhotoAuto', Writable => 'int8u' },
6729             'live-photo.vitality-score' => { Name => 'LivePhotoVitalityScore', Writable => 'float' },
6730             'live-photo.vitality-scoring-version' => { Name => 'LivePhotoVitalityScoringVersion', Writable => 'int64s' },
6731             'apple.photos.variation-identifier' => { Name => 'ApplePhotosVariationIdentifier', Writable => 'int64s' },
6732             'direction.facing' => { Name => 'CameraDirection', Groups => { 2 => 'Location' } },
6733             'direction.motion' => { Name => 'CameraMotion', Groups => { 2 => 'Location' } },
6734             'player.version' => 'PlayerVersion',
6735             'player.movie.visual.brightness'=> 'Brightness',
6736             'player.movie.visual.color' => 'Color',
6737             'player.movie.visual.tint' => 'Tint',
6738             'player.movie.visual.contrast' => 'Contrast',
6739             'player.movie.audio.gain' => 'AudioGain',
6740             'player.movie.audio.treble' => 'Treble',
6741             'player.movie.audio.bass' => 'Bass',
6742             'player.movie.audio.balance' => 'Balance',
6743             'player.movie.audio.pitchshift' => 'PitchShift',
6744             'player.movie.audio.mute' => {
6745             Name => 'Mute',
6746             Format => 'int8u',
6747             PrintConv => { 0 => 'Off', 1 => 'On' },
6748             },
6749             'rating.user' => 'UserRating', # (Canon ELPH 510 HS)
6750             'collection.user' => 'UserCollection', #22
6751             'Encoded_With' => 'EncodedWith',
6752             'content.identifier' => 'ContentIdentifier', #forum14874
6753             'encoder' => { }, # forum15418 (written by ffmpeg)
6754             #
6755             # the following tags aren't in the com.apple.quicktime namespace:
6756             # (Note: must add any non-'com' prefix to %fullKeysID in WriteQuickTime.pl
6757             # to avoid adding the 'com.apple.quicktime' prefix when writing)
6758             #
6759             'com.android.version' => 'AndroidVersion',
6760             'com.android.capture.fps' => { Name => 'AndroidCaptureFPS', Writable => 'float' },
6761             'com.android.manufacturer' => 'AndroidMake',
6762             'com.android.model' => 'AndroidModel',
6763             'com.xiaomi.preview_video_cover' => { Name => 'XiaomiPreviewVideoCover', Writable => 'int32s' },
6764             'com.xiaomi.hdr10' => { Name => 'XiaomiHDR10', Writable => 'int32s' },
6765             'xiaomi.exifInfo.videoinfo' => 'XiaomiExifInfo',
6766             'samsung.android.utc_offset' => { Name => 'AndroidTimeZone', Groups => { 2 => 'Time' } },
6767             #
6768             # also seen
6769             #
6770             # com.divergentmedia.clipwrap.model ('NEX-FS700EK')
6771             # com.divergentmedia.clipwrap.model1 ('49')
6772             # com.divergentmedia.clipwrap.model2 ('0')
6773             # com.divergentmedia.clipwrap.manufacturer ('Sony')
6774             # com.divergentmedia.clipwrap.originalDateTime ('2013/2/6 10:30:40+0200')
6775             #
6776             # seen in timed metadata (mebx), and added dynamically to the table via SaveMetaKeys()
6777             # NOTE: these tags are not writable! (timed metadata cannot yet be written)
6778             #
6779             # (mdta)com.apple.quicktime.video-orientation (dtyp=66, int16s)
6780             'video-orientation' => {
6781             Name => 'VideoOrientation',
6782             Writable => 0,
6783             PrintConv => \%Image::ExifTool::Exif::orientation, #PH (NC)
6784             },
6785             # (mdta)com.apple.quicktime.live-photo-info (dtyp=com.apple.quicktime.com.apple.quicktime.live-photo-info)
6786             'live-photo-info' => {
6787             Name => 'LivePhotoInfo',
6788             Writable => 0,
6789             # not sure what these values mean, but unpack them anyway - PH
6790             # (ignore the fact that the "f" and "l" unpacks won't work on a big-endian machine)
6791             ValueConv => 'join " ",unpack "VfVVf6c4lCCcclf4Vvv", $val',
6792             },
6793             # (mdta)live-photo-still-image-transform mdta (dtyp=com.apple.quicktime.live-photo-still-image-transform)
6794             # (mdta)live-photo-still-image-transform-reference-dimensions (dtyp=com.apple.quicktime.live-photo-still-image-transform-reference-dimensions)
6795             # (mdta)com.apple.quicktime.still-image-time (dtyp=65, int8s)
6796             'still-image-time' => { # (found in live photo)
6797             Name => 'StillImageTime',
6798             Writable => 0,
6799             Notes => q{
6800             this tag always has a value of -1; the time of the still image is obtained
6801             from the associated SampleTime
6802             },
6803             },
6804             # (mdta)com.apple.quicktime.detected-face (dtyp='com.apple.quicktime.detected-face')
6805             'detected-face' => {
6806             Name => 'FaceInfo',
6807             Writable => 0,
6808             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::FaceInfo' },
6809             },
6810             # ---- detected-face fields ( ----
6811             # --> back here after a round trip through FaceInfo -> FaceRec -> FaceItem
6812             # (fiel)com.apple.quicktime.detected-face.bounds (dtyp=80, float[8])
6813             'detected-face.bounds' => {
6814             Name => 'DetectedFaceBounds',
6815             Writable => 0,
6816             # round to a reasonable number of decimal places
6817             PrintConv => 'my @a=split " ",$val;$_=int($_*1e6+.5)/1e6 foreach @a;join " ",@a',
6818             PrintConvInv => '$val',
6819             },
6820             # (fiel)com.apple.quicktime.detected-face.face-id (dtyp=77, int32u)
6821             'detected-face.face-id' => { Name => 'DetectedFaceID', Writable => 0 },
6822             # (fiel)com.apple.quicktime.detected-face.roll-angle (dtyp=23, float)
6823             'detected-face.roll-angle' => { Name => 'DetectedFaceRollAngle', Writable => 0 },
6824             # (fiel)com.apple.quicktime.detected-face.yaw-angle (dtyp=23, float)
6825             'detected-face.yaw-angle' => { Name => 'DetectedFaceYawAngle', Writable => 0 },
6826             # the following tags generated by ShutterEncoder when "preserve metadata" is selected (forum15610)
6827             major_brand => { Name => 'MajorBrand', Avoid => 1 },
6828             minor_version => { Name => 'MinorVersion', Avoid => 1 },
6829             compatible_brands => { Name => 'CompatibleBrands', Avoid => 1 },
6830             creation_time => {
6831             Name => 'CreationTime',
6832             Groups => { 2 => 'Time' },
6833             Avoid => 1,
6834             %iso8601Date,
6835             },
6836             # (mdta)com.apple.quicktime.scene-illuminance
6837             'scene-illuminance' => {
6838             Name => 'SceneIlluminance',
6839             Notes => 'milli-lux',
6840             ValueConv => 'unpack("N", $val)',
6841             Writable => 0, # (don't make this writable because it is found in timed metadata)
6842             },
6843             'full-frame-rate-playback-intent' => 'FullFrameRatePlaybackIntent', #forum16824
6844             'smartstyle-info' => {
6845             Name => 'SmartStyleInfo',
6846             SubDirectory => {
6847             TagTable => 'Image::ExifTool::PLIST::Main',
6848             ProcessProc => 'Image::ExifTool::PLIST::ProcessBinaryPLIST',
6849             },
6850             },
6851             # (mdta) com.apple.quicktime.smartstyle.rendering-version
6852             'smartstyle.rendering-version' => { Name => 'SmartstyleRenderingVersion', Writable => 0 },
6853             # (mdta) com.apple.quicktime.smartstyle.tone
6854             'smartstyle.tone' => { Name => 'SmartstyleTone', Writable => 0 },
6855             # (mdta) com.apple.quicktime.smartstyle.color
6856             'smartstyle.color' => { Name => 'SmartstyleColor', Writable => 0 },
6857             # (mdta) com.apple.quicktime.smartstyle.intensity
6858             'smartstyle.intensity' => { Name => 'SmartstyleIntensity', Writable => 0 },
6859             # (mdta) com.apple.quicktime.smartstyle.bypassed
6860             'smartstyle.bypassed' => { Name => 'SmartstyleBypassed', Writable => 0 },
6861             # (mdta) com.apple.quicktime.smartstyle.cast
6862             'smartstyle.cast' => { Name => 'SmartstyleCast', Writable => 0 },
6863             #
6864             # tags stored directly in the mdta Keys atom
6865             #
6866             setu => {
6867             Name => 'SETU',
6868             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::setu' },
6869             },
6870             sdpd => {
6871             Name => 'SDPD',
6872             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::sdpd' },
6873             },
6874             #
6875             # seen in Apple ProRes RAW file
6876             #
6877             # (mdta)com.apple.proapps.manufacturer (eg. "Sony")
6878             # (mdta)com.apple.proapps.exif.{Exif}.FNumber (float, eg. 1.0)
6879             # (mdta)org.smpte.rdd18.lens.irisfnumber (eg. "F1.0")
6880             # (mdta)com.apple.proapps.exif.{Exif}.ShutterSpeedValue (float, eg. 1.006)
6881             # (mdta)org.smpte.rdd18.camera.shutterspeed_angle (eg. "179.2deg")
6882             # (mdta)org.smpte.rdd18.camera.neutraldensityfilterwheelsetting (eg. "ND1")
6883             # (mdta)org.smpte.rdd18.camera.whitebalance (eg. "4300K")
6884             # (mdta)com.apple.proapps.exif.{Exif}.ExposureIndex (float, eg. 4000)
6885             # (mdta)org.smpte.rdd18.camera.isosensitivity (eg. "4000")
6886             # (mdta)com.apple.proapps.image.{TIFF}.Make (eg. "Atmos")
6887             # (mdta)com.apple.proapps.image.{TIFF}.Model (eg. "ShogunInferno")
6888             # (mdta)com.apple.proapps.image.{TIFF}.Software (eg. "9.0")
6889             );
6890              
6891             # Keys tags in the audio track (ref PH)
6892             %Image::ExifTool::QuickTime::AudioKeys = (
6893             PROCESS_PROC => \&ProcessKeys,
6894             WRITE_PROC => \&WriteKeys,
6895             CHECK_PROC => \&CheckQTValue,
6896             WRITABLE => 1,
6897             GROUPS => { 1 => 'AudioKeys', 2 => 'Audio' },
6898             WRITE_GROUP => 'AudioKeys',
6899             LANG_INFO => \&GetLangInfo,
6900             NOTES => q{
6901             Keys tags written in the audio track by some Apple devices. These tags
6902             belong to the ExifTool AudioKeys family 1 gorup.
6903             },
6904             'player.movie.audio.gain' => 'AudioGain',
6905             'player.movie.audio.treble' => 'Treble',
6906             'player.movie.audio.bass' => 'Bass',
6907             'player.movie.audio.balance' => 'Balance',
6908             'player.movie.audio.pitchshift' => 'PitchShift',
6909             'player.movie.audio.mute' => {
6910             Name => 'Mute',
6911             Format => 'int8u',
6912             PrintConv => { 0 => 'Off', 1 => 'On' },
6913             },
6914             );
6915              
6916             # Keys tags in the video track (ref PH)
6917             %Image::ExifTool::QuickTime::VideoKeys = (
6918             PROCESS_PROC => \&ProcessKeys,
6919             WRITE_PROC => \&WriteKeys,
6920             CHECK_PROC => \&CheckQTValue,
6921             VARS => { LONG_TAGS => 2 },
6922             WRITABLE => 1,
6923             GROUPS => { 1 => 'VideoKeys', 2 => 'Camera' },
6924             WRITE_GROUP => 'VideoKeys',
6925             LANG_INFO => \&GetLangInfo,
6926             NOTES => q{
6927             Keys tags written in the video track. These tags belong to the ExifTool
6928             VideoKeys family 1 gorup.
6929             },
6930             'camera.identifier' => 'CameraIdentifier',
6931             'camera.lens_model' => 'LensModel',
6932             'camera.focal_length.35mm_equivalent' => 'FocalLengthIn35mmFormat',
6933             'camera.framereadouttimeinmicroseconds' => {
6934             Name => 'FrameReadoutTime',
6935             ValueConv => '$val * 1e-6',
6936             ValueConvInv => 'int($val * 1e6 + 0.5)',
6937             PrintConv => '$val * 1e6 . " microseconds"',
6938             PrintConvInv => '$val =~ s/ .*//; $val * 1e-6',
6939             },
6940             'com.apple.photos.captureMode' => 'CaptureMode',
6941             );
6942              
6943             # iTunes info ('----') atoms
6944             %Image::ExifTool::QuickTime::iTunesInfo = (
6945             PROCESS_PROC => \&ProcessMOV,
6946             GROUPS => { 1 => 'iTunes', 2 => 'Audio' },
6947             VARS => { LONG_TAGS => 1 }, # (hack for discrepancy in the way long tags are counted in BuildTagLookup)
6948             NOTES => q{
6949             ExifTool will extract any iTunesInfo tags that exist, even if they are not
6950             defined in this table. These tags belong to the family 1 "iTunes" group,
6951             and are not currently writable.
6952             },
6953             # 'mean'/'name'/'data' atoms form a triplet, but unfortunately
6954             # I haven't been able to find any documentation on this.
6955             # 'mean' is normally 'com.apple.iTunes'
6956             mean => {
6957             Name => 'Mean',
6958             # the 'Triplet' flag tells ProcessMOV() to generate
6959             # a single tag from the mean/name/data triplet
6960             Triplet => 1,
6961             Hidden => 2,
6962             },
6963             name => {
6964             Name => 'Name',
6965             Triplet => 1,
6966             Hidden => 2,
6967             },
6968             data => {
6969             Name => 'Data',
6970             Triplet => 1,
6971             Hidden => 2,
6972             },
6973             # the tag ID's below are composed from "mean/name",
6974             # but "mean/" is omitted if it is "com.apple.iTunes/":
6975             'iTunMOVI' => {
6976             Name => 'iTunMOVI',
6977             SubDirectory => { TagTable => 'Image::ExifTool::PLIST::Main' },
6978             },
6979             'tool' => {
6980             Name => 'iTunTool',
6981             Description => 'iTunTool',
6982             Format => 'int32u',
6983             PrintConv => 'sprintf("0x%.8x",$val)',
6984             },
6985             'iTunEXTC' => {
6986             Name => 'ContentRating',
6987             Notes => 'standard | rating | score | reasons',
6988             # eg. 'us-tv|TV-14|500|V', 'mpaa|PG-13|300|For violence and sexuality'
6989             # (see http://shadowofged.blogspot.ca/2008/06/itunes-content-ratings.html)
6990             },
6991             'iTunNORM' => {
6992             Name => 'VolumeNormalization',
6993             PrintConv => '$val=~s/ 0+(\w)/ $1/g; $val=~s/^\s+//; $val',
6994             },
6995             'iTunSMPB' => {
6996             Name => 'iTunSMPB',
6997             Description => 'iTunSMPB',
6998             # hex format, similar to iTunNORM, but 12 words instead of 10,
6999             # and 4th word is 16 hex digits (all others are 8)
7000             # (gives AAC encoder delay, ref http://code.google.com/p/l-smash/issues/detail?id=1)
7001             PrintConv => '$val=~s/ 0+(\w)/ $1/g; $val=~s/^\s+//; $val',
7002             },
7003             # (CDDB = Compact Disc DataBase)
7004             # iTunes_CDDB_1 = +<# tracks>+...
7005             'iTunes_CDDB_1' => 'CDDB1Info',
7006             'iTunes_CDDB_TrackNumber' => 'CDDBTrackNumber',
7007             'Encoding Params' => {
7008             Name => 'EncodingParams',
7009             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::EncodingParams' },
7010             },
7011             # also heard about 'iTunPGAP', but I haven't seen a sample
7012             # all tags below were added based on samples I have seen - PH
7013             DISCNUMBER => 'DiscNumber',
7014             TRACKNUMBER => 'TrackNumber',
7015             ARTISTS => 'Artists',
7016             CATALOGNUMBER => 'CatalogNumber',
7017             RATING => 'Rating',
7018             MEDIA => 'Media',
7019             SCRIPT => 'Script', # character set? (seen 'Latn')
7020             BARCODE => 'Barcode',
7021             LABEL => 'Label',
7022             MOOD => 'Mood',
7023             DIRECTOR => 'Director',
7024             DIRECTOR_OF_PHOTOGRAPHY => 'DirectorOfPhotography',
7025             PRODUCTION_DESIGNER => 'ProductionDesigner',
7026             COSTUME_DESIGNER => 'CostumeDesigner',
7027             SCREENPLAY_BY => 'ScreenplayBy',
7028             EDITED_BY => 'EditedBy',
7029             PRODUCER => 'Producer',
7030             IMDB_ID => { },
7031             TMDB_ID => { },
7032             Actors => { },
7033             TIPL => { },
7034             popularimeter => 'Popularimeter',
7035             'Dynamic Range (DR)'=> 'DynamicRange',
7036             initialkey => 'InitialKey',
7037             originalyear => 'OriginalYear',
7038             originaldate => 'OriginalDate',
7039             '~length' => 'Length', # play length? (ie. duration?)
7040             replaygain_track_gain=>'ReplayTrackGain',
7041             replaygain_track_peak=>'ReplayTrackPeak',
7042             'Volume Level (ReplayGain)'=> 'ReplayVolumeLevel',
7043             'Dynamic Range (R128)'=> 'DynamicRangeR128',
7044             'Volume Level (R128)' => 'VolumeLevelR128',
7045             'Peak Level (Sample)' => 'PeakLevelSample',
7046             'Peak Level (R128)' => 'PeakLevelR128',
7047             # also seen (many from forum12777):
7048             # 'MusicBrainz Album Release Country'
7049             # 'MusicBrainz Album Type'
7050             # 'MusicBrainz Album Status'
7051             # 'MusicBrainz Track Id'
7052             # 'MusicBrainz Release Track Id'
7053             # 'MusicBrainz Album Id'
7054             # 'MusicBrainz Album Artist Id'
7055             # 'MusicBrainz Artist Id'
7056             # 'Acoustid Id' (sic)
7057             # 'Tool Version'
7058             # 'Tool Name'
7059             # 'ISRC'
7060             # 'HDCD'
7061             # 'Waveform'
7062             );
7063              
7064             # iTunes audio encoding parameters
7065             # ref https://developer.apple.com/library/mac/#documentation/MusicAudio/Reference/AudioCodecServicesRef/Reference/reference.html
7066             %Image::ExifTool::QuickTime::EncodingParams = (
7067             PROCESS_PROC => \&ProcessEncodingParams,
7068             GROUPS => { 2 => 'Audio' },
7069             # (I have commented out the ones that don't have integer values because they
7070             # probably don't appear, and definitely wouldn't work with current decoding - PH)
7071              
7072             # global codec properties
7073             #'lnam' => 'AudioCodecName',
7074             #'lmak' => 'AudioCodecManufacturer',
7075             #'lfor' => 'AudioCodecFormat',
7076             'vpk?' => 'AudioHasVariablePacketByteSizes',
7077             #'ifm#' => 'AudioSupportedInputFormats',
7078             #'ofm#' => 'AudioSupportedOutputFormats',
7079             #'aisr' => 'AudioAvailableInputSampleRates',
7080             #'aosr' => 'AudioAvailableOutputSampleRates',
7081             'abrt' => 'AudioAvailableBitRateRange',
7082             'mnip' => 'AudioMinimumNumberInputPackets',
7083             'mnop' => 'AudioMinimumNumberOutputPackets',
7084             'cmnc' => 'AudioAvailableNumberChannels',
7085             'lmrc' => 'AudioDoesSampleRateConversion',
7086             #'aicl' => 'AudioAvailableInputChannelLayoutTags',
7087             #'aocl' => 'AudioAvailableOutputChannelLayoutTags',
7088             #'if4o' => 'AudioInputFormatsForOutputFormat',
7089             #'of4i' => 'AudioOutputFormatsForInputFormat',
7090             #'acfi' => 'AudioFormatInfo',
7091              
7092             # instance codec properties
7093             'tbuf' => 'AudioInputBufferSize',
7094             'pakf' => 'AudioPacketFrameSize',
7095             'pakb' => 'AudioMaximumPacketByteSize',
7096             #'ifmt' => 'AudioCurrentInputFormat',
7097             #'ofmt' => 'AudioCurrentOutputFormat',
7098             #'kuki' => 'AudioMagicCookie',
7099             'ubuf' => 'AudioUsedInputBufferSize',
7100             'init' => 'AudioIsInitialized',
7101             'brat' => 'AudioCurrentTargetBitRate',
7102             #'cisr' => 'AudioCurrentInputSampleRate',
7103             #'cosr' => 'AudioCurrentOutputSampleRate',
7104             'srcq' => 'AudioQualitySetting',
7105             #'brta' => 'AudioApplicableBitRateRange',
7106             #'isra' => 'AudioApplicableInputSampleRates',
7107             #'osra' => 'AudioApplicableOutputSampleRates',
7108             'pad0' => 'AudioZeroFramesPadded',
7109             'prmm' => 'AudioCodecPrimeMethod',
7110             #'prim' => 'AudioCodecPrimeInfo',
7111             #'icl ' => 'AudioInputChannelLayout',
7112             #'ocl ' => 'AudioOutputChannelLayout',
7113             #'acs ' => 'AudioCodecSettings',
7114             #'acfl' => 'AudioCodecFormatList',
7115             'acbf' => 'AudioBitRateControlMode',
7116             'vbrq' => 'AudioVBRQuality',
7117             'mdel' => 'AudioMinimumDelayMode',
7118              
7119             # deprecated
7120             'pakd' => 'AudioRequiresPacketDescription',
7121             #'brt#' => 'AudioAvailableBitRates',
7122             'acef' => 'AudioExtendFrequencies',
7123             'ursr' => 'AudioUseRecommendedSampleRate',
7124             'oppr' => 'AudioOutputPrecedence',
7125             #'loud' => 'AudioCurrentLoudnessStatistics',
7126              
7127             # others
7128             'vers' => 'AudioEncodingParamsVersion', #PH
7129             'cdcv' => { #PH
7130             Name => 'AudioComponentVersion',
7131             ValueConv => 'join ".", unpack("ncc", pack("N",$val))',
7132             },
7133             );
7134              
7135             # print to video data block
7136             %Image::ExifTool::QuickTime::Video = (
7137             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7138             GROUPS => { 2 => 'Video' },
7139             0 => {
7140             Name => 'DisplaySize',
7141             PrintConv => {
7142             0 => 'Normal',
7143             1 => 'Double Size',
7144             2 => 'Half Size',
7145             3 => 'Full Screen',
7146             4 => 'Current Size',
7147             },
7148             },
7149             6 => {
7150             Name => 'SlideShow',
7151             PrintConv => {
7152             0 => 'No',
7153             1 => 'Yes',
7154             },
7155             },
7156             );
7157              
7158             # 'hnti' atoms
7159             %Image::ExifTool::QuickTime::HintInfo = (
7160             PROCESS_PROC => \&ProcessMOV,
7161             GROUPS => { 2 => 'Video' },
7162             'rtp ' => {
7163             Name => 'RealtimeStreamingProtocol',
7164             PrintConv => '$val=~s/^sdp /(SDP) /; $val',
7165             },
7166             'sdp ' => 'StreamingDataProtocol',
7167             );
7168              
7169             # 'hinf' atoms
7170             %Image::ExifTool::QuickTime::HintTrackInfo = (
7171             PROCESS_PROC => \&ProcessMOV,
7172             GROUPS => { 2 => 'Video' },
7173             trpY => { Name => 'TotalBytes', Format => 'int64u' }, #(documented)
7174             trpy => { Name => 'TotalBytes', Format => 'int64u' }, #(observed)
7175             totl => { Name => 'TotalBytes', Format => 'int32u' },
7176             nump => { Name => 'NumPackets', Format => 'int64u' },
7177             npck => { Name => 'NumPackets', Format => 'int32u' },
7178             tpyl => { Name => 'TotalBytesNoRTPHeaders', Format => 'int64u' },
7179             tpaY => { Name => 'TotalBytesNoRTPHeaders', Format => 'int32u' }, #(documented)
7180             tpay => { Name => 'TotalBytesNoRTPHeaders', Format => 'int32u' }, #(observed)
7181             maxr => {
7182             Name => 'MaxDataRate',
7183             Format => 'int32u',
7184             Count => 2,
7185             PrintConv => 'my @a=split(" ",$val);sprintf("%d bytes in %.3f s",$a[1],$a[0]/1000)',
7186             },
7187             dmed => { Name => 'MediaTrackBytes', Format => 'int64u' },
7188             dimm => { Name => 'ImmediateDataBytes', Format => 'int64u' },
7189             drep => { Name => 'RepeatedDataBytes', Format => 'int64u' },
7190             tmin => {
7191             Name => 'MinTransmissionTime',
7192             Format => 'int32u',
7193             PrintConv => 'sprintf("%.3f s",$val/1000)',
7194             },
7195             tmax => {
7196             Name => 'MaxTransmissionTime',
7197             Format => 'int32u',
7198             PrintConv => 'sprintf("%.3f s",$val/1000)',
7199             },
7200             pmax => { Name => 'LargestPacketSize', Format => 'int32u' },
7201             dmax => {
7202             Name => 'LargestPacketDuration',
7203             Format => 'int32u',
7204             PrintConv => 'sprintf("%.3f s",$val/1000)',
7205             },
7206             payt => {
7207             Name => 'PayloadType',
7208             Format => 'undef', # (necessary to prevent decoding as string!)
7209             ValueConv => 'unpack("N",$val) . " " . substr($val, 5)',
7210             PrintConv => '$val=~s/ /, /;$val',
7211             },
7212             );
7213              
7214             # MP4 media box (ref 5)
7215             %Image::ExifTool::QuickTime::Media = (
7216             PROCESS_PROC => \&ProcessMOV,
7217             WRITE_PROC => \&WriteQuickTime,
7218             GROUPS => { 1 => 'Track#', 2 => 'Video' },
7219             NOTES => 'MP4 media box.',
7220             mdhd => {
7221             Name => 'MediaHeader',
7222             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MediaHeader' },
7223             },
7224             hdlr => {
7225             Name => 'Handler',
7226             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Handler' },
7227             },
7228             minf => {
7229             Name => 'MediaInfo',
7230             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MediaInfo' },
7231             },
7232             elng => 'ExtendedLanguageTag', #29 (NC) eg. "zh-CN"
7233             );
7234              
7235             # MP4 media header box (ref 5)
7236             %Image::ExifTool::QuickTime::MediaHeader = (
7237             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7238             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
7239             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
7240             GROUPS => { 1 => 'Track#', 2 => 'Video' },
7241             FORMAT => 'int32u',
7242             DATAMEMBER => [ 0, 1, 2, 3, 4 ],
7243             0 => {
7244             Name => 'MediaHeaderVersion',
7245             RawConv => '$$self{MediaHeaderVersion} = $val',
7246             },
7247             1 => {
7248             Name => 'MediaCreateDate',
7249             Groups => { 2 => 'Time' },
7250             %timeInfo,
7251             # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
7252             Hook => '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
7253             },
7254             2 => {
7255             Name => 'MediaModifyDate',
7256             Groups => { 2 => 'Time' },
7257             %timeInfo,
7258             # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
7259             Hook => '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
7260             },
7261             3 => {
7262             Name => 'MediaTimeScale',
7263             RawConv => '$$self{MediaTS} = $val',
7264             },
7265             4 => {
7266             Name => 'MediaDuration',
7267             RawConv => '$$self{MediaTS} ? $val / $$self{MediaTS} : $val',
7268             PrintConv => '$$self{MediaTS} ? ConvertDuration($val) : $val',
7269             # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
7270             Hook => '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
7271             },
7272             5 => {
7273             Name => 'MediaLanguageCode',
7274             Format => 'int16u',
7275             RawConv => '$val ? $val : undef',
7276             # allow both Macintosh (for MOV files) and ISO (for MP4 files) language codes
7277             ValueConv => '($val < 0x400 or $val == 0x7fff) ? $val : pack "C*", map { (($val>>$_)&0x1f)+0x60 } 10, 5, 0',
7278             PrintConv => q{
7279             return $val unless $val =~ /^\d+$/;
7280             require Image::ExifTool::Font;
7281             return $Image::ExifTool::Font::ttLang{Macintosh}{$val} || "Unknown ($val)";
7282             },
7283             },
7284             );
7285              
7286             # MP4 media information box (ref 5)
7287             %Image::ExifTool::QuickTime::MediaInfo = (
7288             PROCESS_PROC => \&ProcessMOV,
7289             WRITE_PROC => \&WriteQuickTime,
7290             GROUPS => { 1 => 'Track#', 2 => 'Video' },
7291             NOTES => 'MP4 media info box.',
7292             vmhd => {
7293             Name => 'VideoHeader',
7294             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::VideoHeader' },
7295             },
7296             smhd => {
7297             Name => 'AudioHeader',
7298             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AudioHeader' },
7299             },
7300             hmhd => {
7301             Name => 'HintHeader',
7302             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HintHeader' },
7303             },
7304             nmhd => {
7305             Name => 'NullMediaHeader',
7306             Flags => ['Binary','Unknown'],
7307             },
7308             dinf => {
7309             Name => 'DataInfo', # (don't change this name -- used to recognize directory when writing)
7310             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::DataInfo' },
7311             },
7312             gmhd => {
7313             Name => 'GenMediaHeader',
7314             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::GenMediaHeader' },
7315             },
7316             hdlr => { #PH
7317             Name => 'Handler',
7318             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Handler' },
7319             },
7320             stbl => {
7321             Name => 'SampleTable',
7322             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SampleTable' },
7323             },
7324             );
7325              
7326             # MP4 video media header (ref 5)
7327             %Image::ExifTool::QuickTime::VideoHeader = (
7328             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7329             GROUPS => { 2 => 'Video' },
7330             NOTES => 'MP4 video media header.',
7331             FORMAT => 'int16u',
7332             2 => {
7333             Name => 'GraphicsMode',
7334             PrintHex => 1,
7335             SeparateTable => 'GraphicsMode',
7336             PrintConv => \%graphicsMode,
7337             },
7338             3 => { Name => 'OpColor', Format => 'int16u[3]' },
7339             );
7340              
7341             # MP4 audio media header (ref 5)
7342             %Image::ExifTool::QuickTime::AudioHeader = (
7343             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7344             GROUPS => { 2 => 'Audio' },
7345             NOTES => 'MP4 audio media header.',
7346             FORMAT => 'int16u',
7347             2 => { Name => 'Balance', Format => 'fixed16s' },
7348             );
7349              
7350             # MP4 hint media header (ref 5)
7351             %Image::ExifTool::QuickTime::HintHeader = (
7352             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7353             NOTES => 'MP4 hint media header.',
7354             FORMAT => 'int16u',
7355             2 => 'MaxPDUSize',
7356             3 => 'AvgPDUSize',
7357             4 => { Name => 'MaxBitrate', Format => 'int32u', PrintConv => 'ConvertBitrate($val)' },
7358             6 => { Name => 'AvgBitrate', Format => 'int32u', PrintConv => 'ConvertBitrate($val)' },
7359             );
7360              
7361             # MP4 sample table box (ref 5)
7362             %Image::ExifTool::QuickTime::SampleTable = (
7363             PROCESS_PROC => \&ProcessMOV,
7364             WRITE_PROC => \&WriteQuickTime,
7365             GROUPS => { 2 => 'Video' },
7366             NOTES => 'MP4 sample table box.',
7367             stsd => [
7368             {
7369             Name => 'AudioSampleDesc',
7370             Condition => '$$self{HandlerType} and $$self{HandlerType} eq "soun"',
7371             SubDirectory => {
7372             TagTable => 'Image::ExifTool::QuickTime::AudioSampleDesc',
7373             ProcessProc => \&ProcessSampleDesc,
7374             },
7375             },{
7376             Name => 'VisualSampleDesc',
7377             Condition => '$$self{HandlerType} and $$self{HandlerType} eq "vide"',
7378             SubDirectory => {
7379             TagTable => 'Image::ExifTool::QuickTime::VisualSampleDesc',
7380             ProcessProc => \&ProcessSampleDesc,
7381             },
7382             },{
7383             Name => 'HintSampleDesc',
7384             Condition => '$$self{HandlerType} and $$self{HandlerType} eq "hint"',
7385             SubDirectory => {
7386             TagTable => 'Image::ExifTool::QuickTime::HintSampleDesc',
7387             ProcessProc => \&ProcessSampleDesc,
7388             },
7389             },{
7390             Name => 'MetaSampleDesc',
7391             Condition => '$$self{HandlerType} and $$self{HandlerType} eq "meta"',
7392             SubDirectory => {
7393             TagTable => 'Image::ExifTool::QuickTime::MetaSampleDesc',
7394             ProcessProc => \&ProcessSampleDesc,
7395             },
7396             },{
7397             Name => 'OtherSampleDesc',
7398             SubDirectory => {
7399             TagTable => 'Image::ExifTool::QuickTime::OtherSampleDesc',
7400             ProcessProc => \&ProcessSampleDesc,
7401             },
7402             },
7403             # (Note: "alis" HandlerType handled by the parent audio or video handler)
7404             ],
7405             stts => [ # decoding time-to-sample table
7406             {
7407             Name => 'VideoFrameRate',
7408             Notes => 'average rate calculated from time-to-sample table for video media',
7409             Condition => '$$self{MediaType} eq "vide"',
7410             Format => 'undef', # (necessary to prevent decoding as string!)
7411             # (must be RawConv so appropriate MediaTS is used in calculation)
7412             RawConv => 'Image::ExifTool::QuickTime::CalcSampleRate($self, \$val)',
7413             PrintConv => 'int($val * 1000 + 0.5) / 1000',
7414             },
7415             {
7416             Name => 'TimeToSampleTable',
7417             Format => 'undef',
7418             Flags => ['Binary','Unknown'],
7419             },
7420             ],
7421             ctts => {
7422             Name => 'CompositionTimeToSample',
7423             Flags => ['Binary','Unknown'],
7424             },
7425             stsc => {
7426             Name => 'SampleToChunk',
7427             Flags => ['Binary','Unknown'],
7428             },
7429             stsz => {
7430             Name => 'SampleSizes',
7431             Flags => ['Binary','Unknown'],
7432             },
7433             stz2 => {
7434             Name => 'CompactSampleSizes',
7435             Flags => ['Binary','Unknown'],
7436             },
7437             stco => {
7438             Name => 'ChunkOffset',
7439             Flags => ['Binary','Unknown'],
7440             },
7441             co64 => {
7442             Name => 'ChunkOffset64',
7443             Flags => ['Binary','Unknown'],
7444             },
7445             stss => {
7446             Name => 'SyncSampleTable',
7447             Flags => ['Binary','Unknown'],
7448             },
7449             stsh => {
7450             Name => 'ShadowSyncSampleTable',
7451             Flags => ['Binary','Unknown'],
7452             },
7453             padb => {
7454             Name => 'SamplePaddingBits',
7455             Flags => ['Binary','Unknown'],
7456             },
7457             stdp => {
7458             Name => 'SampleDegradationPriority',
7459             Flags => ['Binary','Unknown'],
7460             },
7461             sdtp => {
7462             Name => 'IdependentAndDisposableSamples',
7463             Flags => ['Binary','Unknown'],
7464             },
7465             sbgp => {
7466             Name => 'SampleToGroup',
7467             Flags => ['Binary','Unknown'],
7468             },
7469             sgpd => {
7470             Name => 'SampleGroupDescription',
7471             Flags => ['Binary','Unknown'],
7472             # bytes 4-7 give grouping type (ref ISO/IEC 14496-15:2014)
7473             # tsas - temporal sublayer sample
7474             # stsa - step-wise temporal layer access
7475             # avss - AVC sample
7476             # tscl - temporal layer scalability
7477             # sync - sync sample
7478             },
7479             subs => {
7480             Name => 'Sub-sampleInformation',
7481             Flags => ['Binary','Unknown'],
7482             },
7483             cslg => {
7484             Name => 'CompositionToDecodeTimelineMapping',
7485             Flags => ['Binary','Unknown'],
7486             },
7487             stps => {
7488             Name => 'PartialSyncSamples',
7489             ValueConv => 'join " ",unpack("x8N*",$val)',
7490             },
7491             # mark - 8 bytes all zero (GoPro)
7492             );
7493              
7494             # MP4 audio sample description box (ref 5/AtomicParsley 0.9.4 parsley.cpp)
7495             %Image::ExifTool::QuickTime::AudioSampleDesc = (
7496             PROCESS_PROC => \&ProcessHybrid,
7497             VARS => { ID_LABEL => 'ID/Index' },
7498             GROUPS => { 2 => 'Audio' },
7499             NOTES => q{
7500             MP4 audio sample description. This hybrid atom contains both data and child
7501             atoms.
7502             },
7503             4 => {
7504             Name => 'AudioFormat',
7505             Format => 'undef[4]',
7506             RawConv => q{
7507             $$self{AudioFormat} = $val;
7508             return undef unless $val =~ /^[\w ]{4}$/i;
7509             # check for protected audio format
7510             $self->OverrideFileType('M4P') if $val eq 'drms' and $$self{FileType} eq 'M4A';
7511             return $val;
7512             },
7513             # see this link for print conversions (not complete):
7514             # https://github.com/yannickcr/brooser/blob/master/php/librairies/getid3/module.audio-video.quicktime.php
7515             },
7516             # 14 - int16u DataReferenceIndex
7517             20 => { #PH
7518             Name => 'AudioVendorID',
7519             Condition => '$$self{AudioFormat} ne "mp4s"',
7520             Format => 'undef[4]',
7521             RawConv => '$val eq "\0\0\0\0" ? undef : $val',
7522             PrintConv => \%vendorID,
7523             SeparateTable => 'VendorID',
7524             },
7525             24 => { Name => 'AudioChannels', Format => 'int16u' },
7526             26 => { Name => 'AudioBitsPerSample', Format => 'int16u' },
7527             32 => { Name => 'AudioSampleRate', Format => 'fixed32u' },
7528             #
7529             # Observed offsets for child atoms of various AudioFormat types:
7530             #
7531             # AudioFormat Offset Child atoms
7532             # ----------- ------ ----------------
7533             # mp4a 52 * wave, chan, esds, SA3D(Insta360 spherical video params?,also GoPro Max and Garmin VIRB 360)
7534             # in24 52 wave, chan
7535             # "ms\0\x11" 52 wave
7536             # sowt 52 chan
7537             # mp4a 36 * esds, pinf
7538             # drms 36 esds, sinf
7539             # samr 36 damr
7540             # alac 36 alac
7541             # ac-3 36 dac3
7542             #
7543             # (* child atoms found at different offsets in mp4a)
7544             #
7545             pinf => {
7546             Name => 'PurchaseInfo',
7547             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ProtectionInfo' },
7548             },
7549             sinf => { # "protection scheme information"
7550             Name => 'ProtectionInfo', #3
7551             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ProtectionInfo' },
7552             },
7553             # f - 16/36 bytes
7554             # esds - 31/40/42/43 bytes - ES descriptor (ref 3)
7555             damr => { #3
7556             Name => 'DecodeConfig',
7557             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::DecodeConfig' },
7558             },
7559             wave => {
7560             Name => 'Wave',
7561             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Wave' },
7562             },
7563             chan => {
7564             Name => 'AudioChannelLayout',
7565             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ChannelLayout' },
7566             },
7567             SA3D => { # written by Garmin VIRB360
7568             Name => 'SpatialAudio',
7569             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SpatialAudio' },
7570             },
7571             btrt => {
7572             Name => 'BitrateInfo',
7573             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Bitrate' },
7574             },
7575             # alac - 28 bytes
7576             # adrm - AAX DRM atom? 148 bytes
7577             # aabd - AAX unknown 17kB (contains 'aavd' strings)
7578             # dapa - ? 203 bytes
7579             );
7580              
7581             # video and image sample description data block
7582             %Image::ExifTool::QuickTime::VisualSampleDesc = (
7583             PROCESS_PROC => \&ProcessHybrid,
7584             VARS => { ID_LABEL => 'ID/Index' },
7585             GROUPS => { 2 => 'Image' },
7586             FORMAT => 'int16u',
7587             2 => {
7588             Name => 'CompressorID',
7589             Format => 'string[4]',
7590             # not very useful since this isn't a complete list and name is given below
7591             # # ref http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html
7592             # PrintConv => {
7593             # cvid => 'Cinepak',
7594             # jpeg => 'JPEG',
7595             # 'smc '=> 'Graphics',
7596             # 'rle '=> 'Animation',
7597             # rpza => 'Apple Video',
7598             # kpcd => 'Kodak Photo CD',
7599             # 'png '=> 'Portable Network Graphics',
7600             # mjpa => 'Motion-JPEG (format A)',
7601             # mjpb => 'Motion-JPEG (format B)',
7602             # SVQ1 => 'Sorenson video, version 1',
7603             # SVQ3 => 'Sorenson video, version 3',
7604             # mp4v => 'MPEG-4 video',
7605             # 'dvc '=> 'NTSC DV-25 video',
7606             # dvcp => 'PAL DV-25 video',
7607             # 'gif '=> 'Compuserve Graphics Interchange Format',
7608             # h263 => 'H.263 video',
7609             # tiff => 'Tagged Image File Format',
7610             # 'raw '=> 'Uncompressed RGB',
7611             # '2vuY'=> "Uncompressed Y'CbCr, 3x8-bit 4:2:2 (2vuY)",
7612             # 'yuv2'=> "Uncompressed Y'CbCr, 3x8-bit 4:2:2 (yuv2)",
7613             # v308 => "Uncompressed Y'CbCr, 8-bit 4:4:4",
7614             # v408 => "Uncompressed Y'CbCr, 8-bit 4:4:4:4",
7615             # v216 => "Uncompressed Y'CbCr, 10, 12, 14, or 16-bit 4:2:2",
7616             # v410 => "Uncompressed Y'CbCr, 10-bit 4:4:4",
7617             # v210 => "Uncompressed Y'CbCr, 10-bit 4:2:2",
7618             # hvc1 => 'HEVC', #PH
7619             # },
7620             },
7621             # 7 - int16u DataReferenceIndex
7622             10 => {
7623             Name => 'VendorID',
7624             Format => 'string[4]',
7625             RawConv => 'length $val ? $val : undef',
7626             PrintConv => \%vendorID,
7627             SeparateTable => 'VendorID',
7628             },
7629             # 14 - ("Quality" in QuickTime docs) ??
7630             16 => 'SourceImageWidth',
7631             17 => 'SourceImageHeight',
7632             18 => { Name => 'XResolution', Format => 'fixed32u' },
7633             20 => { Name => 'YResolution', Format => 'fixed32u' },
7634             # 24 => 'FrameCount', # always 1 (what good is this?)
7635             25 => {
7636             Name => 'CompressorName',
7637             Format => 'string[32]',
7638             # (sometimes this is a Pascal string, and sometimes it is a C string)
7639             RawConv => q{
7640             $val=substr($val,1,ord($1)) if $val=~/^([\0-\x1f])/ and ord($1)
7641             length $val ? $val : undef;
7642             },
7643             },
7644             41 => 'BitDepth',
7645             #
7646             # Observed offsets for child atoms of various CompressorID types:
7647             #
7648             # CompressorID Offset Child atoms
7649             # ----------- ------ ----------------
7650             # avc1 86 avcC, btrt, colr, pasp, fiel, clap, svcC
7651             # mp4v 86 esds, pasp
7652             # s263 86 d263
7653             #
7654             btrt => {
7655             Name => 'BitrateInfo',
7656             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Bitrate' },
7657             },
7658             # Reference for fiel, colr, pasp, clap:
7659             # https://developer.apple.com/library/mac/technotes/tn2162/_index.html#//apple_ref/doc/uid/DTS40013070-CH1-TNTAG9
7660             fiel => {
7661             Name => 'VideoFieldOrder',
7662             ValueConv => 'join(" ", unpack("C*",$val))',
7663             PrintConv => [{
7664             1 => 'Progressive',
7665             2 => '2:1 Interlaced',
7666             }],
7667             },
7668             colr => {
7669             Name => 'ColorRepresentation',
7670             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ColorRep' },
7671             },
7672             pasp => {
7673             Name => 'PixelAspectRatio',
7674             ValueConv => 'join(":", unpack("N*",$val))',
7675             },
7676             clap => {
7677             Name => 'CleanAperture',
7678             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::CleanAperture' },
7679             },
7680             avcC => {
7681             # (see http://thompsonng.blogspot.ca/2010/11/mp4-file-format-part-2.html)
7682             Name => 'AVCConfiguration',
7683             Unknown => 1,
7684             Binary => 1,
7685             },
7686             JPEG => { # (found in CR3 images; used as a flag to identify JpgFromRaw 'vide' stream)
7687             Name => 'JPEGInfo',
7688             # (4 bytes all zero)
7689             Unknown => 1,
7690             Binary => 1,
7691             },
7692             # hvcC - HEVC configuration
7693             # svcC - 7 bytes: 00 00 00 00 ff e0 00
7694             # esds - elementary stream descriptor
7695             # d263
7696             gama => { Name => 'Gamma', Format => 'fixed32u' },
7697             # mjqt - default quantization table for MJPEG
7698             # mjht - default Huffman table for MJPEG
7699             # csgm ? (seen in hevc video)
7700             CMP1 => { # Canon CR3
7701             Name => 'CMP1',
7702             SubDirectory => { TagTable => 'Image::ExifTool::Canon::CMP1' },
7703             },
7704             CDI1 => { # Canon CR3
7705             Name => 'CDI1',
7706             SubDirectory => {
7707             TagTable => 'Image::ExifTool::Canon::CDI1',
7708             Start => 4,
7709             },
7710             },
7711             # JPEG - 4 bytes all 0 (Canon CR3)
7712             # free - (Canon CR3)
7713             #
7714             # spherical video v2 stuff (untested)
7715             #
7716             st3d => {
7717             Name => 'Stereoscopic3D',
7718             Format => 'int8u',
7719             ValueConv => '$val =~ s/.* //; $val', # (remove leading version/flags bytes?)
7720             PrintConv => {
7721             0 => 'Monoscopic',
7722             1 => 'Stereoscopic Top-Bottom',
7723             2 => 'Stereoscopic Left-Right',
7724             3 => 'Stereoscopic Stereo-Custom',
7725             4 => 'Stereoscopic Right-Left',
7726             },
7727             },
7728             sv3d => {
7729             Name => 'SphericalVideo',
7730             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::sv3d' },
7731             },
7732             );
7733              
7734             # MP4 hint sample description box (ref 5)
7735             # (ref https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-SW1)
7736             %Image::ExifTool::QuickTime::HintSampleDesc = (
7737             PROCESS_PROC => \&ProcessHybrid,
7738             VARS => { ID_LABEL => 'ID/Index' },
7739             NOTES => 'MP4 hint sample description.',
7740             4 => { Name => 'HintFormat', Format => 'undef[4]' },
7741             # 14 - int16u DataReferenceIndex
7742             16 => { Name => 'HintTrackVersion', Format => 'int16u' },
7743             # 18 - int16u LastCompatibleHintTrackVersion
7744             20 => { Name => 'MaxPacketSize', Format => 'int32u' },
7745             #
7746             # Observed offsets for child atoms of various HintFormat types:
7747             #
7748             # HintFormat Offset Child atoms
7749             # ----------- ------ ----------------
7750             # "rtp " 24 tims
7751             #
7752             tims => { Name => 'RTPTimeScale', Format => 'int32u' },
7753             tsro => { Name => 'TimestampRandomOffset', Format => 'int32u' },
7754             snro => { Name => 'SequenceNumberRandomOffset', Format => 'int32u' },
7755             );
7756              
7757             # MP4 metadata sample description box
7758             %Image::ExifTool::QuickTime::MetaSampleDesc = (
7759             PROCESS_PROC => \&ProcessHybrid,
7760             NOTES => 'MP4 metadata sample description.',
7761             4 => {
7762             Name => 'MetaFormat',
7763             Format => 'undef[4]',
7764             RawConv => '$$self{MetaFormat} = $val',
7765             },
7766             8 => { # starts at 8 for MetaFormat eq 'camm', and 17 for 'mett'
7767             Name => 'MetaType',
7768             Format => 'undef[$size-8]',
7769             # may start at various locations!
7770             RawConv => '$$self{MetaType} = ($val=~/(application[^\0]+)/ ? $1 : undef)',
7771             },
7772             #
7773             # Observed offsets for child atoms of various MetaFormat types:
7774             #
7775             # MetaFormat Offset Child atoms
7776             # ----------- ------ ----------------
7777             # mebx 24 keys,btrt,lidp,lidl
7778             # fdsc - -
7779             # gpmd - -
7780             # rtmd - -
7781             # CTMD - -
7782             #
7783             'keys' => { #PH (iPhone7+ hevc)
7784             Name => 'Keys',
7785             SubDirectory => {
7786             TagTable => 'Image::ExifTool::QuickTime::Keys',
7787             ProcessProc => \&ProcessMetaKeys,
7788             },
7789             },
7790             btrt => {
7791             Name => 'BitrateInfo',
7792             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Bitrate' },
7793             },
7794             );
7795              
7796             # MP4 generic sample description box
7797             %Image::ExifTool::QuickTime::OtherSampleDesc = (
7798             PROCESS_PROC => \&ProcessHybrid,
7799             4 => {
7800             Name => 'OtherFormat',
7801             Format => 'undef[4]',
7802             RawConv => '$$self{MetaFormat} = $val', # (yes, use MetaFormat for this too)
7803             },
7804             24 => {
7805             Condition => '$$self{MetaFormat} eq "tmcd"',
7806             Name => 'PlaybackFrameRate', # (may differ from recorded FrameRate eg. ../pics/FujiFilmX-H1.mov)
7807             Format => 'rational64u',
7808             },
7809             #
7810             # Observed offsets for child atoms of various OtherFormat types:
7811             #
7812             # OtherFormat Offset Child atoms
7813             # ----------- ------ ----------------
7814             # avc1 86 avcC
7815             # mp4a 36 esds
7816             # mp4s 16 esds
7817             # tmcd 34 name
7818             # data - -
7819             #
7820             ftab => { Name => 'FontTable', Format => 'undef', ValueConv => 'substr($val, 5)' },
7821             name => { Name => 'OtherName', Format => 'undef', ValueConv => 'substr($val, 4)' },
7822             mrlh => { Name => 'MarlinHeader', SubDirectory => { TagTable => 'Image::ExifTool::GM::mrlh' } },
7823             mrlv => { Name => 'MarlinValues', SubDirectory => { TagTable => 'Image::ExifTool::GM::mrlv' } },
7824             mrld => { Name => 'MarlinDictionary',SubDirectory => { TagTable => 'Image::ExifTool::GM::mrld' } },
7825             # tbox - text box (ref 29)
7826             # styl - subtitle style (ref 29)
7827             );
7828              
7829             # AMR decode config box (ref 3)
7830             %Image::ExifTool::QuickTime::DecodeConfig = (
7831             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7832             GROUPS => { 2 => 'Audio' },
7833             0 => {
7834             Name => 'EncoderVendor',
7835             Format => 'undef[4]',
7836             },
7837             4 => 'EncoderVersion',
7838             # 5 - int16u - packet modes
7839             # 7 - int8u - number of packet mode changes
7840             # 8 - int8u - bytes per packet
7841             );
7842              
7843             %Image::ExifTool::QuickTime::ProtectionInfo = (
7844             PROCESS_PROC => \&ProcessMOV,
7845             GROUPS => { 2 => 'Audio' },
7846             NOTES => 'Child atoms found in "sinf" and/or "pinf" atoms.',
7847             frma => 'OriginalFormat',
7848             # imif - IPMP information
7849             schm => {
7850             Name => 'SchemeType',
7851             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SchemeType' },
7852             },
7853             schi => {
7854             Name => 'SchemeInfo',
7855             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SchemeInfo' },
7856             },
7857             enda => {
7858             Name => 'Endianness',
7859             Format => 'int16u',
7860             PrintConv => {
7861             0 => 'Big-endian (Motorola, MM)',
7862             1 => 'Little-endian (Intel, II)',
7863             },
7864             },
7865             # skcr
7866             );
7867              
7868             %Image::ExifTool::QuickTime::Wave = (
7869             PROCESS_PROC => \&ProcessMOV,
7870             frma => 'PurchaseFileFormat',
7871             enda => {
7872             Name => 'Endianness',
7873             Format => 'int16u',
7874             PrintConv => {
7875             0 => 'Big-endian (Motorola, MM)',
7876             1 => 'Little-endian (Intel, II)',
7877             },
7878             },
7879             # "ms\0\x11" - 20 bytes
7880             );
7881              
7882             # audio channel layout (ref CoreAudioTypes.h)
7883             %Image::ExifTool::QuickTime::ChannelLayout = (
7884             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7885             GROUPS => { 2 => 'Audio' },
7886             DATAMEMBER => [ 0, 8 ],
7887             NOTES => 'Audio channel layout.',
7888             # 0 - version and flags
7889             4 => {
7890             Name => 'LayoutFlags',
7891             Format => 'int16u',
7892             RawConv => '$$self{LayoutFlags} = $val',
7893             PrintConvColumns => 2,
7894             PrintConv => {
7895             0 => 'UseDescriptions',
7896             1 => 'UseBitmap',
7897             100 => 'Mono',
7898             101 => 'Stereo',
7899             102 => 'StereoHeadphones',
7900             100 => 'Mono',
7901             101 => 'Stereo',
7902             102 => 'StereoHeadphones',
7903             103 => 'MatrixStereo',
7904             104 => 'MidSide',
7905             105 => 'XY',
7906             106 => 'Binaural',
7907             107 => 'Ambisonic_B_Format',
7908             108 => 'Quadraphonic',
7909             109 => 'Pentagonal',
7910             110 => 'Hexagonal',
7911             111 => 'Octagonal',
7912             112 => 'Cube',
7913             113 => 'MPEG_3_0_A',
7914             114 => 'MPEG_3_0_B',
7915             115 => 'MPEG_4_0_A',
7916             116 => 'MPEG_4_0_B',
7917             117 => 'MPEG_5_0_A',
7918             118 => 'MPEG_5_0_B',
7919             119 => 'MPEG_5_0_C',
7920             120 => 'MPEG_5_0_D',
7921             121 => 'MPEG_5_1_A',
7922             122 => 'MPEG_5_1_B',
7923             123 => 'MPEG_5_1_C',
7924             124 => 'MPEG_5_1_D',
7925             125 => 'MPEG_6_1_A',
7926             126 => 'MPEG_7_1_A',
7927             127 => 'MPEG_7_1_B',
7928             128 => 'MPEG_7_1_C',
7929             129 => 'Emagic_Default_7_1',
7930             130 => 'SMPTE_DTV',
7931             131 => 'ITU_2_1',
7932             132 => 'ITU_2_2',
7933             133 => 'DVD_4',
7934             134 => 'DVD_5',
7935             135 => 'DVD_6',
7936             136 => 'DVD_10',
7937             137 => 'DVD_11',
7938             138 => 'DVD_18',
7939             139 => 'AudioUnit_6_0',
7940             140 => 'AudioUnit_7_0',
7941             141 => 'AAC_6_0',
7942             142 => 'AAC_6_1',
7943             143 => 'AAC_7_0',
7944             144 => 'AAC_Octagonal',
7945             145 => 'TMH_10_2_std',
7946             146 => 'TMH_10_2_full',
7947             147 => 'DiscreteInOrder',
7948             148 => 'AudioUnit_7_0_Front',
7949             149 => 'AC3_1_0_1',
7950             150 => 'AC3_3_0',
7951             151 => 'AC3_3_1',
7952             152 => 'AC3_3_0_1',
7953             153 => 'AC3_2_1_1',
7954             154 => 'AC3_3_1_1',
7955             155 => 'EAC_6_0_A',
7956             156 => 'EAC_7_0_A',
7957             157 => 'EAC3_6_1_A',
7958             158 => 'EAC3_6_1_B',
7959             159 => 'EAC3_6_1_C',
7960             160 => 'EAC3_7_1_A',
7961             161 => 'EAC3_7_1_B',
7962             162 => 'EAC3_7_1_C',
7963             163 => 'EAC3_7_1_D',
7964             164 => 'EAC3_7_1_E',
7965             165 => 'EAC3_7_1_F',
7966             166 => 'EAC3_7_1_G',
7967             167 => 'EAC3_7_1_H',
7968             168 => 'DTS_3_1',
7969             169 => 'DTS_4_1',
7970             170 => 'DTS_6_0_A',
7971             171 => 'DTS_6_0_B',
7972             172 => 'DTS_6_0_C',
7973             173 => 'DTS_6_1_A',
7974             174 => 'DTS_6_1_B',
7975             175 => 'DTS_6_1_C',
7976             176 => 'DTS_7_0',
7977             177 => 'DTS_7_1',
7978             178 => 'DTS_8_0_A',
7979             179 => 'DTS_8_0_B',
7980             180 => 'DTS_8_1_A',
7981             181 => 'DTS_8_1_B',
7982             182 => 'DTS_6_1_D',
7983             183 => 'AAC_7_1_B',
7984             0xffff => 'Unknown',
7985             },
7986             },
7987             6 => {
7988             Name => 'AudioChannels',
7989             Condition => '$$self{LayoutFlags} != 0 and $$self{LayoutFlags} != 1',
7990             Format => 'int16u',
7991             },
7992             8 => {
7993             Name => 'AudioChannelTypes',
7994             Condition => '$$self{LayoutFlags} == 1',
7995             Format => 'int32u',
7996             PrintConv => { BITMASK => {
7997             0 => 'Left',
7998             1 => 'Right',
7999             2 => 'Center',
8000             3 => 'LFEScreen',
8001             4 => 'LeftSurround',
8002             5 => 'RightSurround',
8003             6 => 'LeftCenter',
8004             7 => 'RightCenter',
8005             8 => 'CenterSurround',
8006             9 => 'LeftSurroundDirect',
8007             10 => 'RightSurroundDirect',
8008             11 => 'TopCenterSurround',
8009             12 => 'VerticalHeightLeft',
8010             13 => 'VerticalHeightCenter',
8011             14 => 'VerticalHeightRight',
8012             15 => 'TopBackLeft',
8013             16 => 'TopBackCenter',
8014             17 => 'TopBackRight',
8015             }},
8016             },
8017             12 => {
8018             Name => 'NumChannelDescriptions',
8019             Condition => '$$self{LayoutFlags} == 1',
8020             Format => 'int32u',
8021             RawConv => '$$self{NumChannelDescriptions} = $val',
8022             },
8023             16 => {
8024             Name => 'Channel1Label',
8025             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 0',
8026             Format => 'int32u',
8027             SeparateTable => 'ChannelLabel',
8028             PrintConv => \%channelLabel,
8029             },
8030             20 => {
8031             Name => 'Channel1Flags',
8032             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 0',
8033             Format => 'int32u',
8034             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
8035             },
8036             24 => {
8037             Name => 'Channel1Coordinates',
8038             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 0',
8039             Notes => q{
8040             3 numbers: for rectangular coordinates left/right, back/front, down/up; for
8041             spherical coordinates left/right degrees, down/up degrees, distance
8042             },
8043             Format => 'float[3]',
8044             },
8045             36 => {
8046             Name => 'Channel2Label',
8047             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 1',
8048             Format => 'int32u',
8049             SeparateTable => 'ChannelLabel',
8050             PrintConv => \%channelLabel,
8051             },
8052             40 => {
8053             Name => 'Channel2Flags',
8054             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 1',
8055             Format => 'int32u',
8056             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
8057             },
8058             44 => {
8059             Name => 'Channel2Coordinates',
8060             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 1',
8061             Format => 'float[3]',
8062             },
8063             56 => {
8064             Name => 'Channel3Label',
8065             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 2',
8066             Format => 'int32u',
8067             SeparateTable => 'ChannelLabel',
8068             PrintConv => \%channelLabel,
8069             },
8070             60 => {
8071             Name => 'Channel3Flags',
8072             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 2',
8073             Format => 'int32u',
8074             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
8075             },
8076             64 => {
8077             Name => 'Channel3Coordinates',
8078             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 2',
8079             Format => 'float[3]',
8080             },
8081             76 => {
8082             Name => 'Channel4Label',
8083             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 3',
8084             Format => 'int32u',
8085             SeparateTable => 'ChannelLabel',
8086             PrintConv => \%channelLabel,
8087             },
8088             80 => {
8089             Name => 'Channel4Flags',
8090             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 3',
8091             Format => 'int32u',
8092             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
8093             },
8094             84 => {
8095             Name => 'Channel4Coordinates',
8096             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 3',
8097             Format => 'float[3]',
8098             },
8099             96 => {
8100             Name => 'Channel5Label',
8101             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 4',
8102             Format => 'int32u',
8103             SeparateTable => 'ChannelLabel',
8104             PrintConv => \%channelLabel,
8105             },
8106             100 => {
8107             Name => 'Channel5Flags',
8108             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 4',
8109             Format => 'int32u',
8110             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
8111             },
8112             104 => {
8113             Name => 'Channel5Coordinates',
8114             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 4',
8115             Format => 'float[3]',
8116             },
8117             116 => {
8118             Name => 'Channel6Label',
8119             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 5',
8120             Format => 'int32u',
8121             SeparateTable => 'ChannelLabel',
8122             PrintConv => \%channelLabel,
8123             },
8124             120 => {
8125             Name => 'Channel6Flags',
8126             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 5',
8127             Format => 'int32u',
8128             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
8129             },
8130             124 => {
8131             Name => 'Channel6Coordinates',
8132             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 5',
8133             Format => 'float[3]',
8134             },
8135             136 => {
8136             Name => 'Channel7Label',
8137             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 6',
8138             Format => 'int32u',
8139             SeparateTable => 'ChannelLabel',
8140             PrintConv => \%channelLabel,
8141             },
8142             140 => {
8143             Name => 'Channel7Flags',
8144             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 6',
8145             Format => 'int32u',
8146             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
8147             },
8148             144 => {
8149             Name => 'Channel7Coordinates',
8150             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 6',
8151             Format => 'float[3]',
8152             },
8153             156 => {
8154             Name => 'Channel8Label',
8155             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 7',
8156             Format => 'int32u',
8157             SeparateTable => 'ChannelLabel',
8158             PrintConv => \%channelLabel,
8159             },
8160             160 => {
8161             Name => 'Channel8Flags',
8162             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 7',
8163             Format => 'int32u',
8164             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
8165             },
8166             164 => {
8167             Name => 'Channel8Coordinates',
8168             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 7',
8169             Format => 'float[3]',
8170             },
8171             # (arbitrarily decode only first 8 channels)
8172             );
8173              
8174             # spatial audio (ref https://github.com/google/spatial-media/blob/master/docs/spatial-audio-rfc.md)
8175             %Image::ExifTool::QuickTime::SpatialAudio = (
8176             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
8177             GROUPS => { 2 => 'Audio' },
8178             NOTES => 'Spatial Audio tags.',
8179             0 => 'SpatialAudioVersion',
8180             1 => { Name => 'AmbisonicType', PrintConv => { 0 => 'Periphonic' } },
8181             2 => { Name => 'AmbisonicOrder', Format => 'int32u' },
8182             6 => { Name => 'AmbisonicChannelOrdering', PrintConv => { 0 => 'ACN' } },
8183             7 => { Name => 'AmbisonicNormalization', PrintConv => { 0 => 'SN3D' } },
8184             8 => { Name => 'AmbisonicChannels', Format => 'int32u' },
8185             12 => { Name => 'AmbisonicChannelMap', Format => 'int32u[$val{8}]' },
8186             );
8187              
8188             # scheme type atom
8189             # ref http://xhelmboyx.tripod.com/formats/mp4-layout.txt
8190             %Image::ExifTool::QuickTime::SchemeType = (
8191             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
8192             GROUPS => { 2 => 'Audio' },
8193             # 0 - 4 bytes version
8194             4 => { Name => 'SchemeType', Format => 'undef[4]' },
8195             8 => { Name => 'SchemeVersion', Format => 'int16u' },
8196             10 => { Name => 'SchemeURL', Format => 'string[$size-10]' },
8197             );
8198              
8199             %Image::ExifTool::QuickTime::SchemeInfo = (
8200             PROCESS_PROC => \&ProcessMOV,
8201             GROUPS => { 2 => 'Audio' },
8202             user => {
8203             Name => 'UserID',
8204             Groups => { 2 => 'Author' },
8205             ValueConv => '"0x" . unpack("H*",$val)',
8206             },
8207             cert => { # ref http://www.onvif.org/specs/stream/ONVIF-ExportFileFormat-Spec-v100.pdf
8208             Name => 'Certificate',
8209             ValueConv => '"0x" . unpack("H*",$val)',
8210             },
8211             'key ' => {
8212             Name => 'KeyID',
8213             ValueConv => '"0x" . unpack("H*",$val)',
8214             },
8215             iviv => {
8216             Name => 'InitializationVector',
8217             ValueConv => 'unpack("H*",$val)',
8218             },
8219             righ => {
8220             Name => 'Rights',
8221             Groups => { 2 => 'Author' },
8222             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Rights' },
8223             },
8224             name => { Name => 'UserName', Groups => { 2 => 'Author' } },
8225             # chtb - seen 632 bytes of random data
8226             # priv - private data
8227             # sign
8228             # adkm - Adobe DRM key management system (ref http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf)
8229             # iKMS
8230             # iSFM
8231             # iSLT
8232             );
8233              
8234             %Image::ExifTool::QuickTime::Rights = (
8235             PROCESS_PROC => \&ProcessRights,
8236             GROUPS => { 2 => 'Audio' },
8237             veID => 'ItemVendorID', #PH ("VendorID" ref 19)
8238             plat => 'Platform', #18?
8239             aver => 'VersionRestrictions', #19 ("appversion?" ref 18)
8240             tran => 'TransactionID', #18
8241             song => 'ItemID', #19 ("appid" ref 18)
8242             tool => {
8243             Name => 'ItemTool', #PH (guess) ("itunes build?" ref 18)
8244             Format => 'string',
8245             },
8246             medi => 'MediaFlags', #PH (?)
8247             mode => 'ModeFlags', #PH (?) 0x04 is HD flag (https://compilr.com/heksesang/requiem-mac/UnDrm.java)
8248             # sing - seen 4 zeros
8249             # hi32 - seen "00 00 00 04"
8250             );
8251              
8252             # MP4 data information box (ref 5)
8253             %Image::ExifTool::QuickTime::DataInfo = (
8254             PROCESS_PROC => \&ProcessMOV,
8255             WRITE_PROC => \&WriteQuickTime, # (necessary to parse dref even though we don't change it)
8256             NOTES => 'MP4 data information box.',
8257             dref => {
8258             Name => 'DataRef',
8259             SubDirectory => {
8260             TagTable => 'Image::ExifTool::QuickTime::DataRef',
8261             Start => 8,
8262             },
8263             },
8264             );
8265              
8266             # Generic media header
8267             %Image::ExifTool::QuickTime::GenMediaHeader = (
8268             PROCESS_PROC => \&ProcessMOV,
8269             gmin => {
8270             Name => 'GenMediaInfo',
8271             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::GenMediaInfo' },
8272             },
8273             text => {
8274             Name => 'Text',
8275             Flags => ['Binary','Unknown'],
8276             },
8277             tmcd => {
8278             Name => 'TimeCode',
8279             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TimeCode' },
8280             },
8281             # dbgi - used by DJI - seen "\0\0\0\0"
8282             );
8283              
8284             # TimeCode header
8285             %Image::ExifTool::QuickTime::TimeCode = (
8286             PROCESS_PROC => \&ProcessMOV,
8287             tcmi => {
8288             Name => 'TCMediaInfo',
8289             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TCMediaInfo' },
8290             },
8291             );
8292              
8293             # TimeCode media info (ref 12)
8294             %Image::ExifTool::QuickTime::TCMediaInfo = (
8295             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
8296             GROUPS => { 2 => 'Video' },
8297             4 => {
8298             Name => 'TextFont',
8299             Format => 'int16u',
8300             PrintConv => { 0 => 'System' },
8301             },
8302             6 => {
8303             Name => 'TextFace',
8304             Format => 'int16u',
8305             PrintConv => {
8306             0 => 'Plain',
8307             BITMASK => {
8308             0 => 'Bold',
8309             1 => 'Italic',
8310             2 => 'Underline',
8311             3 => 'Outline',
8312             4 => 'Shadow',
8313             5 => 'Condense',
8314             6 => 'Extend',
8315             },
8316             },
8317             },
8318             8 => {
8319             Name => 'TextSize',
8320             Format => 'int16u',
8321             },
8322             # 10 - reserved
8323             12 => {
8324             Name => 'TextColor',
8325             Format => 'int16u[3]',
8326             },
8327             18 => {
8328             Name => 'BackgroundColor',
8329             Format => 'int16u[3]',
8330             },
8331             24 => {
8332             Name => 'FontName',
8333             Format => 'pstring',
8334             ValueConv => '$self->Decode($val, $self->Options("CharsetQuickTime"))',
8335             },
8336             );
8337              
8338             # Generic media info (ref http://sourceforge.jp/cvs/view/ntvrec/ntvrec/libqtime/gmin.h?view=co)
8339             %Image::ExifTool::QuickTime::GenMediaInfo = (
8340             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
8341             GROUPS => { 2 => 'Video' },
8342             0 => 'GenMediaVersion',
8343             1 => { Name => 'GenFlags', Format => 'int8u[3]' },
8344             4 => { Name => 'GenGraphicsMode',
8345             Format => 'int16u',
8346             PrintHex => 1,
8347             SeparateTable => 'GraphicsMode',
8348             PrintConv => \%graphicsMode,
8349             },
8350             6 => { Name => 'GenOpColor', Format => 'int16u[3]' },
8351             12 => { Name => 'GenBalance', Format => 'fixed16s' },
8352             );
8353              
8354             # MP4 data reference box (ref 5)
8355             %Image::ExifTool::QuickTime::DataRef = (
8356             PROCESS_PROC => \&ProcessMOV,
8357             WRITE_PROC => \&WriteQuickTime, # (necessary to parse dref even though we don't change it)
8358             NOTES => 'MP4 data reference box.',
8359             'url ' => {
8360             Name => 'URL',
8361             Format => 'undef', # (necessary to prevent decoding as string!)
8362             RawConv => q{
8363             # ignore if self-contained (flags bit 0 set)
8364             return undef if unpack("N",$val) & 0x01;
8365             $_ = substr($val,4); s/\0.*//s; $_;
8366             },
8367             },
8368             "url\0" => { # (written by GoPro)
8369             Name => 'URL',
8370             Format => 'undef', # (necessary to prevent decoding as string!)
8371             RawConv => q{
8372             # ignore if self-contained (flags bit 0 set)
8373             return undef if unpack("N",$val) & 0x01;
8374             $_ = substr($val,4); s/\0.*//s; $_;
8375             },
8376             },
8377             'urn ' => {
8378             Name => 'URN',
8379             Format => 'undef', # (necessary to prevent decoding as string!)
8380             RawConv => q{
8381             return undef if unpack("N",$val) & 0x01;
8382             $_ = substr($val,4); s/\0+/; /; s/\0.*//s; $_;
8383             },
8384             },
8385             );
8386              
8387             # MP4 handler box (ref 5)
8388             %Image::ExifTool::QuickTime::Handler = (
8389             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
8390             GROUPS => { 2 => 'Video' },
8391             4 => { #PH
8392             Name => 'HandlerClass',
8393             Format => 'undef[4]',
8394             RawConv => '$val eq "\0\0\0\0" ? undef : $val',
8395             PrintConv => {
8396             mhlr => 'Media Handler',
8397             dhlr => 'Data Handler',
8398             },
8399             },
8400             8 => {
8401             Name => 'HandlerType',
8402             Format => 'undef[4]',
8403             RawConv => q{
8404             unless ($$self{HasHandler}{$val} or not $Image::ExifTool::QuickTime::eeBox{$val}
8405             or $val eq 'vide' or $$self{OPTIONS}{ExtractEmbedded} or $$self{OPTIONS}{Validate})
8406             {
8407             Image::ExifTool::QuickTime::EEWarn($self);
8408             }
8409             $$self{HandlerType} = $val unless $val eq 'alis' or $val eq 'url ';
8410             $$self{MediaType} = $val if @{$$self{PATH}} > 1 and $$self{PATH}[-2] eq 'Media';
8411             $$self{HasHandler}{$val} = 1; # remember all our handlers
8412             return $val;
8413             },
8414             PrintConvColumns => 2,
8415             PrintConv => {
8416             alis => 'Alias Data', #PH
8417             crsm => 'Clock Reference', #3
8418             hint => 'Hint Track',
8419             ipsm => 'IPMP', #3
8420             m7sm => 'MPEG-7 Stream', #3
8421             meta => 'NRT Metadata', #PH
8422             mdir => 'Metadata', #3
8423             mdta => 'Metadata Tags', #PH
8424             mjsm => 'MPEG-J', #3
8425             ocsm => 'Object Content', #3
8426             odsm => 'Object Descriptor', #3
8427             priv => 'Private', #PH
8428             sdsm => 'Scene Description', #3
8429             soun => 'Audio Track',
8430             text => 'Text', #PH (but what type? subtitle?)
8431             tmcd => 'Time Code', #PH
8432             'url '=> 'URL', #3
8433             vide => 'Video Track',
8434             subp => 'Subpicture', #http://www.google.nl/patents/US7778526
8435             nrtm => 'Non-Real Time Metadata', #PH (Sony ILCE-7S) [how is this different from "meta"?]
8436             pict => 'Picture', # (HEIC images)
8437             camm => 'Camera Metadata', # (Insta360 MP4)
8438             psmd => 'Panasonic Static Metadata', #PH (Leica C-Lux CAM-DC25)
8439             data => 'Data', #PH (GPS and G-sensor data from DataKam)
8440             sbtl => 'Subtitle', #PH (TomTom Bandit Action Cam)
8441             },
8442             },
8443             12 => { #PH
8444             Name => 'HandlerVendorID',
8445             Format => 'undef[4]',
8446             RawConv => '$val eq "\0\0\0\0" ? undef : $val',
8447             PrintConv => \%vendorID,
8448             SeparateTable => 'VendorID',
8449             },
8450             24 => {
8451             Name => 'HandlerDescription',
8452             Format => 'string',
8453             # (sometimes this is a Pascal string, and sometimes it is a C string)
8454             RawConv => q{
8455             $val=substr($val,1,ord($1)) if $val=~/^([\0-\x1f])/ and ord($1)
8456             length $val ? $val : undef;
8457             },
8458             },
8459             );
8460              
8461             # Flip uuid data (ref PH)
8462             %Image::ExifTool::QuickTime::Flip = (
8463             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
8464             FORMAT => 'int32u',
8465             FIRST_ENTRY => 0,
8466             NOTES => 'Found in MP4 files from Flip Video cameras.',
8467             GROUPS => { 1 => 'MakerNotes', 2 => 'Image' },
8468             1 => 'PreviewImageWidth',
8469             2 => 'PreviewImageHeight',
8470             13 => 'PreviewImageLength',
8471             14 => { # (confirmed for FlipVideoMinoHD)
8472             Name => 'SerialNumber',
8473             Groups => { 2 => 'Camera' },
8474             Format => 'string[16]',
8475             },
8476             28 => {
8477             Name => 'PreviewImage',
8478             Groups => { 2 => 'Preview' },
8479             Format => 'undef[$val{13}]',
8480             RawConv => '$self->ValidateImage(\$val, $tag)',
8481             },
8482             );
8483              
8484             # atoms in Pittasoft "free" atom
8485             %Image::ExifTool::QuickTime::Pittasoft = (
8486             PROCESS_PROC => \&ProcessMOV,
8487             NOTES => 'Tags found in Pittasoft Blackvue dashcam "free" data.',
8488             cprt => 'Copyright',
8489             thum => {
8490             Name => 'PreviewImage',
8491             Groups => { 2 => 'Preview' },
8492             Binary => 1,
8493             RawConv => q{
8494             return undef unless length $val > 4;
8495             my $len = unpack('N', $val);
8496             return undef unless length $val >= 4 + $len;
8497             return substr($val, 4, $len);
8498             },
8499             },
8500             ptnm => {
8501             Name => 'OriginalFileName',
8502             ValueConv => 'substr($val, 4, -1)',
8503             },
8504             ptrh => {
8505             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Pittasoft' },
8506             # contains these atoms:
8507             # ptvi - 27 bytes: '..avc1...'
8508             # ptso - 16 bytes: '..mp4a...'
8509             },
8510             'gps ' => {
8511             Name => 'GPSLog',
8512             Binary => 1, # (ASCII NMEA track log with leading timestamps)
8513             Notes => 'parsed to extract GPS separately when ExtractEmbedded is used',
8514             RawConv => q{
8515             $val =~ s/\0+$//; # remove trailing nulls
8516             if (length $val and $$self{OPTIONS}{ExtractEmbedded}) {
8517             my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
8518             Image::ExifTool::QuickTime::ProcessGPSLog($self, { DataPt => \$val }, $tagTbl);
8519             }
8520             return $val;
8521             },
8522             },
8523             '3gf ' => {
8524             Name => 'AccelData',
8525             SubDirectory => {
8526             TagTable => 'Image::ExifTool::QuickTime::Stream',
8527             ProcessProc => \&Process_3gf,
8528             },
8529             },
8530             sttm => {
8531             Name => 'StartTime',
8532             Format => 'int64u',
8533             Groups => { 2 => 'Time' },
8534             RawConv => '$$self{StartTime} = $val',
8535             # (ms since Jan 1, 1970, in local time zone - PH)
8536             ValueConv => q{
8537             my $secs = int($val / 1000);
8538             return ConvertUnixTime($secs) . sprintf(".%03d",$val - $secs * 1000);
8539             },
8540             PrintConv => '$self->ConvertDateTime($val)',
8541             },
8542             );
8543              
8544             # Nextbase tags (ref PH)
8545             %Image::ExifTool::QuickTime::Nextbase = (
8546             GROUPS => { 1 => 'Nextbase', 2 => 'Camera' },
8547             PROCESS_PROC => \&ProcessNextbase,
8548             WRITE_PROC => \&WriteNextbase,
8549             VARS => { LONG_TAGS => 3 },
8550             NOTES => q{
8551             Tags found in 'infi' atom from some Nextbase videos. As well as these tags,
8552             other existing tags are also extracted. These tags are not currently
8553             writable but they may all be removed by deleting the Nextbase group.
8554             },
8555             'Wi-Fi SSID' => { },
8556             'Wi-Fi Password' => { },
8557             'Wi-Fi MAC Address' => { },
8558             'Model' => { },
8559             'Firmware' => { },
8560             'Serial No' => { Name => 'SerialNumber' },
8561             'FCC-ID' => { },
8562             'Battery Status' => { },
8563             'SD Card Manf ID' => { },
8564             'SD Card OEM ID' => { },
8565             'SD Card Model No' => { },
8566             'SD Card Serial No' => { },
8567             'SD Card Manf Date' => { },
8568             'SD Card Type' => { },
8569             'SD Card Used Space' => { },
8570             'SD Card Class' => { },
8571             'SD Card Size' => { },
8572             'SD Card Format' => { },
8573             'Wi-Fi SSID' => { },
8574             'Wi-Fi Password' => { },
8575             'Wi-Fi MAC Address' => { },
8576             'Bluetooth Name' => { },
8577             'Bluetooth MAC Address' => { },
8578             'Resolution' => { },
8579             'Exposure' => { },
8580             'Video Length' => { },
8581             'Audio' => { },
8582             'Time Stamp' => { Name => 'VideoTimeStamp' },
8583             'Speed Stamp' => { },
8584             'GPS Stamp' => { },
8585             'Model Stamp' => { },
8586             'Dual Files' => { },
8587             'Time Lapse' => { },
8588             'Number / License Plate' => { },
8589             'G Sensor' => { },
8590             'Image Stabilisation' => { },
8591             'Extreme Weather Mode' => { },
8592             'Screen Saver' => { },
8593             'Alerts' => { },
8594             'Recording History' => { },
8595             'Parking Mode' => { },
8596             'Language' => { },
8597             'Country' => { },
8598             'Time Zone / DST' => { Groups => { 2 => 'Time' } },
8599             'Time & Date' => { Name => 'TimeAndDate', Groups => { 2 => 'Time' } },
8600             'Speed Units' => { },
8601             'Device Sounds' => { },
8602             'Screen Dimming' => { },
8603             'Auto Power Off' => { },
8604             'Keep User Settings' => { },
8605             'System Info' => { },
8606             'Format SD Card' => { },
8607             'Default Settings' => { },
8608             'Emergency SOS' => { },
8609             'Reversing Camera' => { },
8610             'what3words' => { Name => 'What3Words' },
8611             'MyNextbase - Pairing' => { },
8612             'MyNextbase - Paired Device Name' => { },
8613             'Alexa' => { },
8614             'Alexa - Pairing' => { },
8615             'Alexa - Paired Device Name' => { },
8616             'Alexa - Privacy Mode' => { },
8617             'Alexa - Wake Word Language' => { },
8618             'Firmware Version' => { },
8619             'RTOS' => { },
8620             'Linux' => { },
8621             'NBCD' => { },
8622             'Alexa' => { },
8623             '2nd Cam' => { Name => 'SecondCam' },
8624             );
8625              
8626             # QuickTime composite tags
8627             %Image::ExifTool::QuickTime::Composite = (
8628             GROUPS => { 2 => 'Video' },
8629             Rotation => {
8630             Notes => q{
8631             degrees of clockwise camera rotation. Writing this tag updates QuickTime
8632             MatrixStructure for all tracks with a non-zero image size
8633             },
8634             Require => {
8635             0 => 'QuickTime:MatrixStructure',
8636             1 => 'QuickTime:HandlerType',
8637             },
8638             Writable => 1,
8639             Protected => 1,
8640             WriteAlso => {
8641             MatrixStructure => 'Image::ExifTool::QuickTime::GetRotationMatrix($val)',
8642             },
8643             ValueConv => 'Image::ExifTool::QuickTime::CalcRotation($self)',
8644             ValueConvInv => '$val',
8645             },
8646             AvgBitrate => {
8647             Priority => 0, # let QuickTime::AvgBitrate take priority
8648             Require => {
8649             0 => 'QuickTime::MediaDataSize',
8650             1 => 'QuickTime::Duration',
8651             },
8652             RawConv => q{
8653             return undef unless $val[1];
8654             $val[1] /= $$self{TimeScale} if $$self{TimeScale};
8655             my $key = 'MediaDataSize';
8656             my $size = $val[0];
8657             for (;;) {
8658             $key = $self->NextTagKey($key) or last;
8659             $size += $self->GetValue($key, 'ValueConv');
8660             }
8661             return int($size * 8 / $val[1] + 0.5);
8662             },
8663             PrintConv => 'ConvertBitrate($val)',
8664             },
8665             GPSLatitude => {
8666             Require => 'QuickTime:GPSCoordinates',
8667             Groups => { 2 => 'Location' },
8668             ValueConv => 'my @c = split " ", $val; $c[0]',
8669             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
8670             },
8671             GPSLongitude => {
8672             Require => 'QuickTime:GPSCoordinates',
8673             Groups => { 2 => 'Location' },
8674             ValueConv => 'my @c = split " ", $val; $c[1]',
8675             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
8676             },
8677             # split altitude into GPSAltitude/GPSAltitudeRef like EXIF and XMP
8678             GPSAltitude => {
8679             Require => 'QuickTime:GPSCoordinates',
8680             Groups => { 2 => 'Location' },
8681             Priority => 0, # (because it may not exist)
8682             ValueConv => 'my @c = split " ", $val; defined $c[2] ? abs($c[2]) : undef',
8683             PrintConv => '"$val m"',
8684             },
8685             GPSAltitudeRef => {
8686             Require => 'QuickTime:GPSCoordinates',
8687             Groups => { 2 => 'Location' },
8688             Priority => 0, # (because altitude information may not exist)
8689             ValueConv => 'my @c = split " ", $val; defined $c[2] ? ($c[2] < 0 ? 1 : 0) : undef',
8690             PrintConv => {
8691             0 => 'Above Sea Level',
8692             1 => 'Below Sea Level',
8693             },
8694             },
8695             GPSLatitude2 => {
8696             Name => 'GPSLatitude',
8697             Require => 'QuickTime:LocationInformation',
8698             Groups => { 2 => 'Location' },
8699             ValueConv => '$val =~ /Lat=([-+.\d]+)/ ? $1 : undef',
8700             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
8701             },
8702             GPSLongitude2 => {
8703             Name => 'GPSLongitude',
8704             Require => 'QuickTime:LocationInformation',
8705             Groups => { 2 => 'Location' },
8706             ValueConv => '$val =~ /Lon=([-+.\d]+)/ ? $1 : undef',
8707             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
8708             },
8709             GPSAltitude2 => {
8710             Name => 'GPSAltitude',
8711             Require => 'QuickTime:LocationInformation',
8712             Groups => { 2 => 'Location' },
8713             ValueConv => '$val =~ /Alt=([-+.\d]+)/ ? abs($1) : undef',
8714             PrintConv => '"$val m"',
8715             },
8716             GPSAltitudeRef2 => {
8717             Name => 'GPSAltitudeRef',
8718             Require => 'QuickTime:LocationInformation',
8719             Groups => { 2 => 'Location' },
8720             ValueConv => '$val =~ /Alt=([-+.\d]+)/ ? ($1 < 0 ? 1 : 0) : undef',
8721             PrintConv => {
8722             0 => 'Above Sea Level',
8723             1 => 'Below Sea Level',
8724             },
8725             },
8726             CDDBDiscPlayTime => {
8727             Require => 'CDDB1Info',
8728             Groups => { 2 => 'Audio' },
8729             ValueConv => '$val =~ /^..([a-z0-9]{4})/i ? hex($1) : undef',
8730             PrintConv => 'ConvertDuration($val)',
8731             },
8732             CDDBDiscTracks => {
8733             Require => 'CDDB1Info',
8734             Groups => { 2 => 'Audio' },
8735             ValueConv => '$val =~ /^.{6}([a-z0-9]{2})/i ? hex($1) : undef',
8736             },
8737             );
8738              
8739             # add our composite tags
8740             Image::ExifTool::AddCompositeTags('Image::ExifTool::QuickTime');
8741              
8742              
8743             #------------------------------------------------------------------------------
8744             # AutoLoad our routines when necessary
8745             #
8746             sub AUTOLOAD
8747             {
8748             # (Note: no need to autoload routines in QuickTimeStream that use Stream table)
8749 23 50   23   100 if ($AUTOLOAD eq 'Image::ExifTool::QuickTime::Process_mebx') {
8750 0         0 require 'Image/ExifTool/QuickTimeStream.pl';
8751 31     31   379 no strict 'refs';
  31         71  
  31         438458  
8752 0         0 return &$AUTOLOAD(@_);
8753             } else {
8754 23         261 return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
8755             }
8756             }
8757              
8758             #------------------------------------------------------------------------------
8759             # Get rotation matrix
8760             # Inputs: 0) angle in degrees
8761             # Returns: 9-element rotation matrix as a string (with 0 x/y offsets)
8762             sub GetRotationMatrix($)
8763             {
8764 1     1 0 4 my $ang = 3.14159265358979323846264 * shift() / 180;
8765 1         15 my $cos = cos $ang;
8766 1         3 my $sin = sin $ang;
8767             # round to zero
8768 1 50       5 $cos = 0 if abs($cos) < 1e-12;
8769 1 50       4 $sin = 0 if abs($sin) < 1e-12;
8770 1         2 my $msn = -$sin;
8771 1         10 return "$cos $sin 0 $msn $cos 0 0 0 1";
8772             }
8773              
8774             #------------------------------------------------------------------------------
8775             # Get rotation angle from a matrix
8776             # Inputs: 0) rotation matrix as a string
8777             # Return: positive rotation angle in degrees rounded to 3 decimal points,
8778             # or undef on error
8779             sub GetRotationAngle($)
8780             {
8781 5     5 0 11 my $rotMatrix = shift;
8782 5         22 my @a = split ' ', $rotMatrix;
8783 5 50 66     22 return undef if $a[0]==0 and $a[1]==0;
8784             # calculate the rotation angle (assume uniform rotation)
8785 5         29 my $angle = atan2($a[1], $a[0]) * 180 / 3.14159;
8786 5 100       14 $angle += 360 if $angle < 0;
8787 5         43 return int($angle * 1000 + 0.5) / 1000;
8788             }
8789              
8790             #------------------------------------------------------------------------------
8791             # Calculate rotation of video track
8792             # Inputs: 0) ExifTool object ref
8793             # Returns: rotation angle or undef
8794             sub CalcRotation($)
8795             {
8796 5     5 0 10 my $et = shift;
8797 5         10 my $value = $$et{VALUE};
8798 5         10 my ($i, $track);
8799             # get the video track family 1 group (eg. "Track1");
8800 5         12 for ($i=0; ; ++$i) {
8801 11 100       24 my $idx = $i ? " ($i)" : '';
8802 11         16 my $tag = "HandlerType$idx";
8803 11 100       41 last unless $$value{$tag};
8804 10 100       30 next unless $$value{$tag} eq 'vide';
8805 4         19 $track = $et->GetGroup($tag, 1);
8806 4         8 last;
8807             }
8808 5 100       18 return undef unless $track;
8809             # get the video track matrix
8810 4         10 for ($i=0; ; ++$i) {
8811 12 100       23 my $idx = $i ? " ($i)" : '';
8812 12         17 my $tag = "MatrixStructure$idx";
8813 12 50       26 last unless $$value{$tag};
8814 12 100       23 next unless $et->GetGroup($tag, 1) eq $track;
8815 4         18 return GetRotationAngle($$value{$tag});
8816             }
8817 0         0 return undef;
8818             }
8819              
8820             #------------------------------------------------------------------------------
8821             # Get MatrixStructure for a given rotation angle
8822             # Inputs: 0) rotation angle (deg), 1) ExifTool ref
8823             # Returns: matrix structure as a string, or undef if it can't be rotated
8824             # - requires ImageSizeLookahead to determine the video image size, and doesn't
8825             # rotate matrix unless image size is valid
8826             sub GetMatrixStructure($$)
8827             {
8828 2     2 0 5 my ($val, $et) = @_;
8829 2         11 my @a = split ' ', $val;
8830             # pass straight through if it already has an offset
8831 2 50 33     10 return $val unless $a[6] == 0 and $a[7] == 0;
8832 2         12 my @s = split ' ', $$et{ImageSizeLookahead};
8833 2         9 my ($w, $h) = @s[12,13];
8834 2 100 66     11 return undef unless $w and $h; # don't rotate 0-sized track
8835 1         5 $_ = Image::ExifTool::QuickTime::FixWrongFormat($_) foreach $w,$h;
8836             # apply necessary offsets for the standard rotations
8837 1         4 my $angle = GetRotationAngle($val);
8838 1 50       4 return undef unless defined $angle;
8839 1 50       8 if ($angle == 90) {
    50          
    50          
8840 0         0 @a[6,7] = ($h, 0);
8841             } elsif ($angle == 180) {
8842 0         0 @a[6,7] = ($w, $h);
8843             } elsif ($angle == 270) {
8844 1         3 @a[6,7] = (0, $w);
8845             }
8846 1         6 return "@a";
8847             }
8848              
8849             #------------------------------------------------------------------------------
8850             # Determine the average sample rate from a time-to-sample table
8851             # Inputs: 0) ExifTool object ref, 1) time-to-sample table data ref
8852             # Returns: average sample rate (in Hz)
8853             sub CalcSampleRate($$)
8854             {
8855 26     26 0 72 my ($et, $valPt) = @_;
8856 26         88 my @dat = unpack('N*', $$valPt);
8857 26         75 my ($num, $dur) = (0, 0);
8858 26         41 my $i;
8859 26         103 for ($i=2; $i<@dat-1; $i+=2) {
8860 26         59 $num += $dat[$i]; # total number of samples
8861 26         91 $dur += $dat[$i] * $dat[$i+1]; # total sample duration
8862             }
8863 26 50 33     172 return undef unless $num and $dur and $$et{MediaTS};
      33        
8864 26         246 return $num * $$et{MediaTS} / $dur;
8865             }
8866              
8867             #------------------------------------------------------------------------------
8868             # Fix incorrect format for ImageWidth/Height as written by Pentax
8869             sub FixWrongFormat($)
8870             {
8871 98     98 0 156 my $val = shift;
8872 98 100       276 return undef unless $val;
8873 54 50       215 return $val & 0xfff00000 ? unpack('n',pack('N',$val)) : $val;
8874             }
8875              
8876             #------------------------------------------------------------------------------
8877             # Convert ISO 6709 string to standard lag/lon format
8878             # Inputs: 0) ISO 6709 string (lat, lon, and optional alt)
8879             # Returns: position in decimal degrees with altitude if available
8880             # Notes: Wikipedia indicates altitude may be in feet -- how is this specified?
8881             sub ConvertISO6709($)
8882             {
8883 0     0 0 0 my $val = shift;
8884 0 0       0 if ($val =~ /^([-+]\d{1,2}(?:\.\d*)?)([-+]\d{1,3}(?:\.\d*)?)([-+]\d+(?:\.\d*)?)?/) {
    0          
    0          
8885             # +DD.DDD+DDD.DDD+AA.AAA
8886 0         0 $val = ($1 + 0) . ' ' . ($2 + 0);
8887 0 0       0 $val .= ' ' . ($3 + 0) if $3;
8888             } elsif ($val =~ /^([-+])(\d{2})(\d{2}(?:\.\d*)?)([-+])(\d{3})(\d{2}(?:\.\d*)?)([-+]\d+(?:\.\d*)?)?/) {
8889             # +DDMM.MMM+DDDMM.MMM+AA.AAA
8890 0         0 my $lat = $2 + $3 / 60;
8891 0 0       0 $lat = -$lat if $1 eq '-';
8892 0         0 my $lon = $5 + $6 / 60;
8893 0 0       0 $lon = -$lon if $4 eq '-';
8894 0         0 $val = "$lat $lon";
8895 0 0       0 $val .= ' ' . ($7 + 0) if $7;
8896             } elsif ($val =~ /^([-+])(\d{2})(\d{2})(\d{2}(?:\.\d*)?)([-+])(\d{3})(\d{2})(\d{2}(?:\.\d*)?)([-+]\d+(?:\.\d*)?)?/) {
8897             # +DDMMSS.SSS+DDDMMSS.SSS+AA.AAA
8898 0         0 my $lat = $2 + $3 / 60 + $4 / 3600;
8899 0 0       0 $lat = -$lat if $1 eq '-';
8900 0         0 my $lon = $6 + $7 / 60 + $8 / 3600;
8901 0 0       0 $lon = -$lon if $5 eq '-';
8902 0         0 $val = "$lat $lon";
8903 0 0       0 $val .= ' ' . ($9 + 0) if $9;
8904             }
8905 0         0 return $val;
8906             }
8907              
8908             #------------------------------------------------------------------------------
8909             # Convert Nero chapter list (ref ffmpeg libavformat/movenc.c)
8910             # Inputs: 0) binary chpl data
8911             # Returns: chapter list
8912             sub ConvertChapterList($)
8913             {
8914 0     0 0 0 my $val = shift;
8915 0         0 my $size = length $val;
8916 0 0       0 return '' if $size < 9;
8917 0         0 my $num = Get8u(\$val, 8);
8918 0         0 my ($i, @chapters);
8919 0         0 my $pos = 9;
8920 0         0 for ($i=0; $i<$num; ++$i) {
8921 0 0       0 last if $pos + 9 > $size;
8922 0         0 my $dur = Get64u(\$val, $pos) / 10000000;
8923 0         0 my $len = Get8u(\$val, $pos + 8);
8924 0 0       0 last if $pos + 9 + $len > $size;
8925 0         0 my $title = substr($val, $pos + 9, $len);
8926 0         0 $pos += 9 + $len;
8927 0         0 push @chapters, "$dur $title";
8928             }
8929 0         0 return \@chapters; # return as a list
8930             }
8931              
8932             #------------------------------------------------------------------------------
8933             # Print conversion for a Nero chapter list item
8934             # Inputs: 0) ValueConv chapter string
8935             # Returns: formatted chapter string
8936             sub PrintChapter($)
8937             {
8938 0     0 0 0 my $val = shift;
8939 0 0       0 $val =~ /^(\S+) (.*)/ or return $val;
8940 0         0 my ($dur, $title) = ($1, $2);
8941 0         0 my $h = int($dur / 3600);
8942 0         0 $dur -= $h * 3600;
8943 0         0 my $m = int($dur / 60);
8944 0         0 my $s = $dur - $m * 60;
8945 0         0 my $ss = sprintf('%06.3f', $s);
8946 0 0       0 if ($ss >= 60) {
8947 0         0 $ss = '00.000';
8948 0 0       0 ++$m >= 60 and $m -= 60, ++$h;
8949             }
8950 0         0 return sprintf("[%d:%.2d:%s] %s",$h,$m,$ss,$title);
8951             }
8952              
8953             #------------------------------------------------------------------------------
8954             # Format GPSCoordinates for printing
8955             # Inputs: 0) string with numerical lat, lon and optional alt, separated by spaces
8956             # 1) ExifTool object reference
8957             # Returns: PrintConv value
8958             sub PrintGPSCoordinates($)
8959             {
8960 0     0 0 0 my ($val, $et) = @_;
8961 0         0 my @v = split ' ', $val;
8962 0         0 my $prt = Image::ExifTool::GPS::ToDMS($et, $v[0], 1, "N") . ', ' .
8963             Image::ExifTool::GPS::ToDMS($et, $v[1], 1, "E");
8964 0 0       0 if (defined $v[2]) {
8965 0 0       0 $prt .= ', ' . ($v[2] < 0 ? -$v[2] . ' m Below' : $v[2] . ' m Above') . ' Sea Level';
8966             }
8967 0         0 return $prt;
8968             }
8969              
8970             #------------------------------------------------------------------------------
8971             # Unpack packed ISO 639/T language code
8972             # Inputs: 0) packed language code (or undef/0), 1) true to not treat 'und' and 'eng' as default
8973             # Returns: language code, or undef/0 for default language, or 'err' for format error
8974             sub UnpackLang($;$)
8975             {
8976 17     17 0 31 my ($lang, $noDef) = @_;
8977 17 50       30 if ($lang) {
8978             # language code is packed in 5-bit characters
8979 17         29 $lang = pack 'C*', map { (($lang>>$_)&0x1f)+0x60 } 10, 5, 0;
  51         97  
8980             # validate language code
8981 17 50       65 if ($lang =~ /^[a-z]+$/) {
8982             # treat 'eng' or 'und' as the default language
8983 17 100 100     91 undef $lang if ($lang eq 'und' or $lang eq 'eng') and not $noDef;
      100        
8984             } else {
8985 0         0 $lang = 'err'; # invalid language code
8986             }
8987             }
8988 17         31 return $lang;
8989             }
8990              
8991             #------------------------------------------------------------------------------
8992             # Get language code string given QuickTime language and country codes
8993             # Inputs: 0) numerical language code, 1) numerical country code, 2) no defaults
8994             # Returns: language code string (ie. "fra-FR") or undef for default language
8995             # ex) 0x15c7 0x0000 is 'eng' with no country (ie. returns 'und' unless $noDef)
8996             # 0x15c7 0x5553 is 'eng-US'
8997             # 0x1a41 0x4652 is 'fra-FR'
8998             # 0x55c4 is 'und'
8999             sub GetLangCode($;$$)
9000             {
9001 11     11 0 23 my ($lang, $ctry, $noDef) = @_;
9002             # ignore country ('ctry') and language lists ('lang') for now
9003 11 50 66     35 undef $ctry if $ctry and $ctry <= 255;
9004 11 50 33     33 undef $lang if $lang and $lang <= 255;
9005 11         32 my $langCode = UnpackLang($lang, $noDef);
9006             # add country code if specified
9007 11 100       19 if ($ctry) {
9008 5         16 $ctry = unpack('a2',pack('n',$ctry)); # unpack as ISO 3166-1
9009             # treat 'ZZ' like a default country (see ref 12)
9010 5 50       17 undef $ctry if $ctry eq 'ZZ';
9011 5 50 33     26 if ($ctry and $ctry =~ /^[A-Z]{2}$/) {
9012 5 100 50     14 $langCode or $langCode = UnpackLang($lang,1) || 'und';
9013 5         8 $langCode .= "-$ctry";
9014             }
9015             }
9016 11         21 return $langCode;
9017             }
9018              
9019             #------------------------------------------------------------------------------
9020             # Get langInfo hash and save details about alt-lang tags
9021             # Inputs: 0) ExifTool ref, 1) tagInfo hash ref, 2) locale code
9022             # Returns: new tagInfo hash ref, or undef if invalid
9023             sub GetLangInfoQT($$$)
9024             {
9025 3     3 0 6 my ($et, $tagInfo, $langCode) = @_;
9026 3         15 my $langInfo = Image::ExifTool::GetLangInfo($tagInfo, $langCode);
9027 3 50       5 if ($langInfo) {
9028 3 100       13 $$et{QTLang} or $$et{QTLang} = [ ];
9029 3         3 push @{$$et{QTLang}}, $$langInfo{Name};
  3         10  
9030             }
9031 3         5 return $langInfo;
9032             }
9033              
9034             #------------------------------------------------------------------------------
9035             # Get variable-length integer from data (used by ParseItemLocation)
9036             # Inputs: 0) data ref, 1) start position, 2) integer size in bytes (0, 4 or 8),
9037             # 3) default value
9038             # Returns: integer value, and updates current position
9039             sub GetVarInt($$$;$)
9040             {
9041 78     78 0 99 my ($dataPt, $pos, $n, $default) = @_;
9042 78         75 my $len = length $$dataPt;
9043 78         91 $_[1] = $pos + $n; # update current position
9044 78 50       110 return undef if $pos + $n > $len;
9045 78 50       103 if ($n == 0) {
    50          
    0          
9046 0   0     0 return $default || 0;
9047             } elsif ($n == 4) {
9048 78         105 return Get32u($dataPt, $pos);
9049             } elsif ($n == 8) {
9050 0         0 return Get64u($dataPt, $pos);
9051             }
9052 0         0 return undef;
9053             }
9054              
9055             #------------------------------------------------------------------------------
9056             # Get null-terminated string from binary data (used by ParseItemInfoEntry)
9057             # Inputs: 0) data ref, 1) start position
9058             # Returns: string, and updates current position
9059             sub GetString($$)
9060             {
9061 30     30 0 43 my ($dataPt, $pos) = @_;
9062 30         32 my $len = length $$dataPt;
9063 30         34 my $str = '';
9064 30         47 while ($pos < $len) {
9065 240         241 my $ch = substr($$dataPt, $pos, 1);
9066 240         239 ++$pos;
9067 240 100       280 last if ord($ch) == 0;
9068 215         295 $str .= $ch;
9069             }
9070 30         34 $_[1] = $pos; # update current position
9071 30         92 return $str;
9072             }
9073              
9074             #------------------------------------------------------------------------------
9075             # Get a printable version of the tag ID
9076             # Inputs: 0) tag ID, 1) Flag: 0x01 - print as 4- or 8-digit hex value if necessary
9077             # 0x02 - put leading backslash before escaped character
9078             # Returns: Printable tag ID
9079             sub PrintableTagID($;$)
9080             {
9081 20     20 0 35 my $tag = $_[0];
9082 20         46 my $n = ($tag =~ s/([^-_a-zA-Z0-9])/'x'.unpack('H*',$1)/eg);
  0         0  
9083 20 0 33     43 if ($n and $_[1]) {
9084 0 0 0     0 if ($n > 2 and $_[1] & 0x01) {
    0          
9085 0         0 $tag = '0x' . unpack('H8', $_[0]);
9086 0         0 $tag =~ s/^0x0000/0x/;
9087             } elsif ($_[1] & 0x02) {
9088 0         0 ($tag = $_[0]) =~ s/([^-_a-zA-Z0-9])/'\\x'.unpack('H*',$1)/eg;
  0         0  
9089             }
9090             }
9091 20         39 return $tag;
9092             }
9093              
9094             #==============================================================================
9095             # The following ParseXxx routines parse various boxes to extract this
9096             # information about embedded items in a $$et{ItemInfo} hash, keyed by item ID:
9097             #
9098             # iloc:
9099             # ConstructionMethod - offset type: 0=file, 1=idat, 2=item
9100             # DataReferenceIndex - 0 for "this file", otherwise index in dref box
9101             # BaseOffset - base for file offsets
9102             # Extents - list of details for data in file:
9103             # 0) index (extent_index)
9104             # 1) offset (extent_offset)
9105             # 2) length (extent_length)
9106             # 3) nlen (length_size)
9107             # 4) lenPt (pointer to length word)
9108             # infe:
9109             # ProtectionIndex - index if item is protected (0 for unprotected)
9110             # Name - item name
9111             # ContentType - mime type of item
9112             # ContentEncoding - item encoding
9113             # URI - URI of a 'uri '-type item
9114             # infe - raw data for 'infe' box (when writing only) [retracted]
9115             # ipma:
9116             # Association - list of associated properties in the ipco container
9117             # Essential - list of "essential" flags for the associated properties
9118             # cdsc:
9119             # RefersTo - hash lookup of flags based on referred item ID
9120             # other:
9121             # DocNum - exiftool document number for this item
9122             #
9123             #------------------------------------------------------------------------------
9124             # Parse item location (iloc) box (ref ISO 14496-12:2015 pg.79)
9125             # Inputs: 0) iloc data, 1) ExifTool ref
9126             # Returns: undef, and fills in ExifTool ItemInfo hash
9127             # Notes: see also Handle_iloc() in WriteQuickTime.pl
9128             sub ParseItemLocation($$)
9129             {
9130 6     6 0 39 my ($val, $et) = @_;
9131 6         18 my ($i, $j, $num, $pos, $id);
9132 6         0 my ($extent_index, $extent_offset, $extent_length);
9133              
9134 6 100       25 my $verbose = $$et{IsWriting} ? 0 : $et->Options('Verbose');
9135 6   50     39 my $items = $$et{ItemInfo} || ($$et{ItemInfo} = { });
9136 6         12 my $len = length $val;
9137 6 50       20 return undef if $len < 8;
9138 6         14 my $ver = Get8u(\$val, 0);
9139 6         14 my $siz = Get16u(\$val, 4);
9140 6         11 my $noff = ($siz >> 12);
9141 6         13 my $nlen = ($siz >> 8) & 0x0f;
9142 6         14 my $nbas = ($siz >> 4) & 0x0f;
9143 6         8 my $nind = $siz & 0x0f;
9144 6 50       15 if ($ver < 2) {
9145 6         12 $num = Get16u(\$val, 6);
9146 6         13 $pos = 8;
9147             } else {
9148 0 0       0 return undef if $len < 10;
9149 0         0 $num = Get32u(\$val, 6);
9150 0         0 $pos = 10;
9151             }
9152 6         17 for ($i=0; $i<$num; ++$i) {
9153 20 50       30 if ($ver < 2) {
9154 20 50       33 return undef if $pos + 2 > $len;
9155 20         33 $id = Get16u(\$val, $pos);
9156 20         24 $pos += 2;
9157             } else {
9158 0 0       0 return undef if $pos + 4 > $len;
9159 0         0 $id = Get32u(\$val, $pos);
9160 0         0 $pos += 4;
9161             }
9162 20 50 33     61 if ($ver == 1 or $ver == 2) {
9163 0 0       0 return undef if $pos + 2 > $len;
9164 0         0 $$items{$id}{ConstructionMethod} = Get16u(\$val, $pos) & 0x0f;
9165 0         0 $pos += 2;
9166             }
9167 20 50       35 return undef if $pos + 2 > $len;
9168 20         33 $$items{$id}{DataReferenceIndex} = Get16u(\$val, $pos);
9169 20         24 $pos += 2;
9170 20         30 $$items{$id}{BaseOffset} = GetVarInt(\$val, $pos, $nbas);
9171 20 50       35 return undef if $pos + 2 > $len;
9172 20         32 my $ext_num = Get16u(\$val, $pos);
9173 20         22 $pos += 2;
9174 20         23 my @extents;
9175 20         34 for ($j=0; $j<$ext_num; ++$j) {
9176 20 50 33     59 if ($ver == 1 or $ver == 2) {
9177 0         0 $extent_index = GetVarInt(\$val, $pos, $nind, 1);
9178             }
9179 20         30 $extent_offset = GetVarInt(\$val, $pos, $noff);
9180 20         28 $extent_length = GetVarInt(\$val, $pos, $nlen);
9181 20 50       31 return undef unless defined $extent_length;
9182             $et->VPrint(1, "$$et{INDENT} Item $id: const_meth=",
9183             defined $$items{$id}{ConstructionMethod} ? $$items{$id}{ConstructionMethod} : '',
9184             sprintf(" base=0x%x offset=0x%x len=0x%x\n", $$items{$id}{BaseOffset},
9185 20 0       37 $extent_offset, $extent_length)) if $verbose;
    50          
9186 20         55 push @extents, [ $extent_index, $extent_offset, $extent_length, $nlen, $pos-$nlen ];
9187             }
9188             # save item location information keyed on 1-based item ID:
9189 20         67 $$items{$id}{Extents} = \@extents;
9190             }
9191 6         17 return undef;
9192             }
9193              
9194             #------------------------------------------------------------------------------
9195             # Parse content describes entry (cdsc) box
9196             # Inputs: 0) cdsc data, 1) ExifTool ref
9197             # Returns: undef, and fills in ExifTool ItemInfo hash
9198             sub ParseContentDescribes($$)
9199             {
9200 8     8 0 12 my ($val, $et) = @_;
9201 8         11 my ($id, $count, @to);
9202 8 50       22 if ($$et{ItemRefVersion}) {
9203 0 0       0 return undef if length $val < 10;
9204 0         0 ($id, $count, @to) = unpack('NnN*', $val);
9205             } else {
9206 8 50       17 return undef if length $val < 6;
9207 8         20 ($id, $count, @to) = unpack('nnn*', $val);
9208             }
9209 8 50       25 if ($count > @to) {
    50          
9210 0         0 my $str = 'Missing values in ContentDescribes box';
9211 0 0       0 $$et{IsWriting} ? $et->Error($str) : $et->Warn($str);
9212             } elsif ($count < @to) {
9213 0         0 $et->Warn('Ignored extra values in ContentDescribes box', 1);
9214 0         0 @to = $count;
9215             }
9216             # add all referenced item ID's to a "RefersTo" lookup
9217 8         39 $$et{ItemInfo}{$id}{RefersTo}{$_} = 1 foreach @to;
9218 8         14 return undef;
9219             }
9220              
9221             #------------------------------------------------------------------------------
9222             # Parse item information entry (infe) box (ref ISO 14496-12:2015 pg.82)
9223             # Inputs: 0) infe data, 1) ExifTool ref
9224             # Returns: undef, and fills in ExifTool ItemInfo hash
9225             sub ParseItemInfoEntry($$)
9226             {
9227 20     20 0 41 my ($val, $et) = @_;
9228 20         25 my $id;
9229              
9230 20 100       57 my $verbose = $$et{IsWriting} ? 0 : $et->Options('Verbose');
9231 20   50     44 my $items = $$et{ItemInfo} || ($$et{ItemInfo} = { });
9232 20         26 my $len = length $val;
9233 20 50       37 return undef if $len < 4;
9234 20         66 my $ver = Get8u(\$val, 0);
9235 20         26 my $pos = 4;
9236 20 50       47 return undef if $pos + 4 > $len;
9237 20 50 33     57 if ($ver == 0 or $ver == 1) {
9238 0         0 $id = Get16u(\$val, $pos);
9239 0         0 $$items{$id}{ProtectionIndex} = Get16u(\$val, $pos + 2);
9240 0         0 $pos += 4;
9241 0         0 $$items{$id}{Name} = GetString(\$val, $pos);
9242 0         0 $$items{$id}{ContentType} = GetString(\$val, $pos);
9243 0         0 $$items{$id}{ContentEncoding} = GetString(\$val, $pos);
9244             } else {
9245 20 50       35 if ($ver == 2) {
    0          
9246 20         39 $id = Get16u(\$val, $pos);
9247 20         28 $pos += 2;
9248             } elsif ($ver == 3) {
9249 0         0 $id = Get32u(\$val, $pos);
9250 0         0 $pos += 4;
9251             }
9252 20 50       63 return undef if $pos + 6 > $len;
9253 20         32 $$items{$id}{ProtectionIndex} = Get16u(\$val, $pos);
9254 20         31 my $type = substr($val, $pos + 2, 4);
9255 20         41 $$items{$id}{Type} = $type;
9256 20         28 $pos += 6;
9257 20         47 $$items{$id}{Name} = GetString(\$val, $pos);
9258 20 100       57 if ($type eq 'mime') {
    50          
9259 5         14 $$items{$id}{ContentType} = GetString(\$val, $pos);
9260 5         11 $$items{$id}{ContentEncoding} = GetString(\$val, $pos);
9261             } elsif ($type eq 'uri ') {
9262 0         0 $$items{$id}{URI} = GetString(\$val, $pos);
9263             }
9264             }
9265             #[retracted] # save raw infe box when writing in case we need to sort items later
9266             #[retracted] $$items{$id}{infe} = pack('N', length($val)+8) . 'infe' . $val if $$et{IsWriting};
9267             $et->VPrint(1, "$$et{INDENT} Item $id: Type=", $$items{$id}{Type} || '',
9268             ' Name=', $$items{$id}{Name} || '',
9269             ' ContentType=', $$items{$id}{ContentType} || '',
9270 20 0 0     36 ($$et{PrimaryItem} and $$et{PrimaryItem} == $id) ? ' (PrimaryItem)' : '',
    50 0        
      0        
      0        
9271             "\n") if $verbose > 1;
9272 20 50       45 unless ($id > $$et{LastItemID}) {
9273 0         0 $et->Warn('Item info entries are out of order'); #[retracted] unless $$et{IsWriting};
9274             #[retracted] $$et{ItemsNotSorted} = 1; # set flag indicating the items weren't sorted
9275             }
9276 20         29 $$et{LastItemID} = $id;
9277 20         37 return undef;
9278             }
9279              
9280             #------------------------------------------------------------------------------
9281             # Parse item property association (ipma) box (ref https://github.com/gpac/gpac/blob/master/src/isomedia/iff.c)
9282             # Inputs: 0) ipma data, 1) ExifTool ref
9283             # Returns: undef, and fills in ExifTool ItemInfo hash
9284             sub ParseItemPropAssoc($$)
9285             {
9286 6     6 0 15 my ($val, $et) = @_;
9287 6         12 my ($i, $j, $id);
9288              
9289 6 100       21 my $verbose = $$et{IsWriting} ? 0 : $et->Options('Verbose');
9290 6   50     18 my $items = $$et{ItemInfo} || ($$et{ItemInfo} = { });
9291 6         11 my $len = length $val;
9292 6 50       18 return undef if $len < 8;
9293 6         15 my $ver = Get8u(\$val, 0);
9294 6         19 my $flg = Get32u(\$val, 0);
9295 6         12 my $num = Get32u(\$val, 4);
9296 6         9 my $pos = 8;
9297 6         12 my $lastID = -1;
9298 6         22 for ($i=0; $i<$num; ++$i) {
9299 12 50       20 if ($ver == 0) {
9300 12 50       25 return undef if $pos + 3 > $len;
9301 12         23 $id = Get16u(\$val, $pos);
9302 12         19 $pos += 2;
9303             } else {
9304 0 0       0 return undef if $pos + 5 > $len;
9305 0         0 $id = Get32u(\$val, $pos);
9306 0         0 $pos += 4;
9307             }
9308 12         20 my $n = Get8u(\$val, $pos++);
9309 12         23 my (@association, @essential);
9310 12 50       19 if ($flg & 0x01) {
9311 0 0       0 return undef if $pos + $n * 2 > $len;
9312 0         0 for ($j=0; $j<$n; ++$j) {
9313 0         0 my $tmp = Get16u(\$val, $pos + $j * 2);
9314 0         0 push @association, $tmp & 0x7fff;
9315 0 0       0 push @essential, ($tmp & 0x8000) ? 1 : 0;
9316             }
9317 0         0 $pos += $n * 2;
9318             } else {
9319 12 50       21 return undef if $pos + $n > $len;
9320 12         26 for ($j=0; $j<$n; ++$j) {
9321 24         40 my $tmp = Get8u(\$val, $pos + $j);
9322 24         36 push @association, $tmp & 0x7f;
9323 24 100       66 push @essential, ($tmp & 0x80) ? 1 : 0;
9324             }
9325 12         17 $pos += $n;
9326             }
9327 12         44 $$items{$id}{Association} = \@association;
9328 12         25 $$items{$id}{Essential} = \@essential;
9329 12 50       27 $et->VPrint(1, "$$et{INDENT} Item $id properties: @association\n") if $verbose > 1;
9330             # (according to ISO/IEC 23008-12, these entries must be sorted by item ID)
9331 12 50       22 $et->Warn('Item property association entries are out of order') unless $id > $lastID;
9332 12         29 $lastID = $id;
9333             }
9334 6         13 return undef;
9335             }
9336              
9337             #------------------------------------------------------------------------------
9338             # Process item information now
9339             # Inputs: 0) ExifTool ref
9340             sub HandleItemInfo($)
9341             {
9342 62     62 0 107 my $et = shift;
9343 62         160 my $raf = $$et{RAF};
9344 62         123 my $items = $$et{ItemInfo};
9345 62         169 my $verbose = $et->Options('Verbose');
9346 62         93 my $buff;
9347              
9348             # extract information from EXIF/XMP metadata items
9349 62 100 66     169 if ($items and $raf) {
9350 3         6 push @{$$et{PATH}}, 'ItemInformation';
  3         7  
9351 3         8 my $curPos = $raf->Tell();
9352 3         8 my $primary = $$et{PrimaryItem};
9353 3         6 my $id;
9354 3         17 $et->VerboseDir('Processing items from ItemInformation', scalar(keys %$items));
9355 3         16 foreach $id (sort { $a <=> $b } keys %$items) {
  11         21  
9356 11         14 my $item = $$items{$id};
9357 11   50     38 my $type = $$item{ContentType} || $$item{Type} || next;
9358 11 50       31 if ($verbose) {
9359             # add up total length of this item for the verbose output
9360 0         0 my $len = 0;
9361 0 0 0     0 if ($$item{Extents} and @{$$item{Extents}}) {
  0         0  
9362 0         0 $len += $$_[2] foreach @{$$item{Extents}};
  0         0  
9363             }
9364 0 0       0 my $enc = $$item{ContentEncoding} ? ", $$item{ContentEncoding} encoded" : '';
9365 0         0 $et->VPrint(0, "$$et{INDENT}Item $id) '${type}' ($len bytes$enc)\n");
9366             }
9367             # get ExifTool name for this item
9368             my $name = { Exif => 'EXIF', 'application/rdf+xml' => 'XMP', jpeg => 'PreviewImage',
9369 11   100     46 'uri ' => 'PLIST' }->{$type} || '';
9370 11         34 my ($warn, $extent);
9371 11 50       18 if ($$item{ContentEncoding}) {
9372 0 0       0 if ($$item{ContentEncoding} ne 'deflate') {
    0          
9373             # (other possible values are 'gzip' and 'compress', but I don't have samples of these)
9374 0         0 $warn = "Can't currently decode $$item{ContentEncoding} encoded $type metadata";
9375 0         0 } elsif (not eval { require Compress::Zlib }) {
9376 0         0 $warn = "Install Compress::Zlib to decode deflated $type metadata";
9377             }
9378             }
9379 11 50       20 $warn = "Can't currently decode protected $type metadata" if $$item{ProtectionIndex};
9380             # Note: In HEIC's, these seem to indicate data in 'idat' instead of 'mdat'
9381 11   50     26 my $constMeth = $$item{ConstructionMethod} || 0;
9382 11 50       22 $warn = "Can't currently extract $type with construction method $constMeth" if $constMeth > 1;
9383 11 50 33     18 $warn = "No 'idat' for $type object with construction method 1" if $constMeth == 1 and not $$et{MediaDataInfo};
9384 11 50 33     21 $et->Warn($warn) if $warn and $name;
9385 11 50       25 $warn = 'Not this file' if $$item{DataReferenceIndex}; # (can only extract from "this file")
9386 11 50 33     30 unless (($$item{Extents} and @{$$item{Extents}}) or $warn) {
  11   33     25  
9387 0         0 $warn = "No Extents for $type item";
9388 0 0       0 $et->Warn($warn) if $name;
9389             }
9390 11 50       17 if ($warn) {
9391 0 0       0 $et->VPrint(0, "$$et{INDENT} [not extracted] ($warn)\n") if $verbose > 2;
9392 0         0 next;
9393             }
9394 11 50 100     29 my $base = ($$item{BaseOffset} || 0) + ($constMeth ? $$et{MediaDataInfo}[0] : 0);
9395 11 50       19 if ($verbose > 2) {
9396             # do verbose hex dump
9397 0         0 my $len = 0;
9398 0         0 undef $buff;
9399 0         0 my $val = '';
9400 0 0       0 my $maxLen = $verbose > 3 ? 2048 : 96;
9401 0         0 foreach $extent (@{$$item{Extents}}) {
  0         0  
9402 0         0 my $n = $$extent[2];
9403 0         0 my $more = $maxLen - $len;
9404 0 0 0     0 if ($more > 0 and $n) {
9405 0 0       0 $more = $n if $more > $n;
9406 0 0       0 $val .= $buff if defined $buff;
9407 0 0       0 $raf->Seek($$extent[1] + $base, 0) or last;
9408 0 0       0 $raf->Read($buff, $more) or last;
9409             }
9410 0         0 $len += $n;
9411             }
9412 0 0       0 if (defined $buff) {
9413 0 0       0 $buff = $val . $buff if length $val;
9414 0         0 $et->VerboseDump(\$buff, DataPos => $$item{Extents}[0][1] + $base);
9415 0         0 my $snip = $len - length $buff;
9416 0 0       0 $et->VPrint(0, "$$et{INDENT} [snip $snip bytes]\n") if $snip;
9417             }
9418             }
9419             # do hash of AVIF "av01" and HEIC image data
9420 11 50 66     42 if ($isImageData{$type} and $$et{ImageDataHash}) {
9421 0         0 my $hash = $$et{ImageDataHash};
9422 0         0 my $tot = 0;
9423 0         0 foreach $extent (@{$$item{Extents}}) {
  0         0  
9424 0 0       0 $raf->Seek($$extent[1] + $base, 0) or $et->Warn("Seek error in $type image data"), last;
9425 0         0 $tot += $et->ImageDataHash($raf, $$extent[2], "$type image", 1);
9426             }
9427 0 0       0 $et->VPrint(0, "$$et{INDENT}(ImageDataHash: $tot bytes of $type data)\n") if $tot;
9428             }
9429 11 100       21 next unless $name;
9430             # assemble the data for this item
9431 5         6 undef $buff;
9432 5         8 my $val = '';
9433 5         7 foreach $extent (@{$$item{Extents}}) {
  5         11  
9434 5 50       17 $val .= $buff if defined $buff;
9435 5 50       16 $raf->Seek($$extent[1] + $base, 0) or last;
9436 5 100       19 $raf->Read($buff, $$extent[2]) or last;
9437             }
9438 5 50       11 next unless defined $buff;
9439 5 50       10 $buff = $val . $buff if length $val;
9440 5 100       8 next unless length $buff; # ignore empty directories
9441 4 50       12 if ($$item{ContentEncoding}) {
9442 0         0 my ($v2, $stat);
9443 0         0 my $inflate = Compress::Zlib::inflateInit();
9444 0 0       0 $inflate and ($v2, $stat) = $inflate->inflate($buff);
9445 0 0 0     0 if ($inflate and $stat == Compress::Zlib::Z_STREAM_END()) {
9446 0         0 $buff = $v2;
9447 0         0 my $len = length $buff;
9448 0         0 $et->VPrint(0, "$$et{INDENT}Inflated Item $id) '${type}' ($len bytes)\n");
9449 0         0 $et->VerboseDump(\$buff);
9450             } else {
9451 0         0 $warn = "Error inflating $name metadata";
9452 0         0 $et->Warn($warn);
9453 0 0       0 $et->VPrint(0, "$$et{INDENT} [not extracted] ($warn)\n") if $verbose > 2;
9454 0         0 next;
9455             }
9456             }
9457 4         5 my ($start, $subTable, $proc);
9458 4         12 my $pos = $$item{Extents}[0][1] + $base;
9459 4 100 66     24 if ($name eq 'EXIF' and length $buff >= 4) {
    50          
    50          
9460 2 50       11 if ($buff =~ /^(MM\0\x2a|II\x2a\0)/) {
    50          
9461 0         0 $et->Warn('Missing Exif header');
9462             } elsif ($buff =~ /^Exif\0\0/) {
9463             # (haven't seen this yet, but it is just a matter of time
9464             # until someone screws it up like this)
9465 0         0 $et->Warn('Missing Exif header size');
9466 0         0 $start = 6;
9467             } else {
9468 2         6 my $n = unpack('N', $buff);
9469 2         2 $start = 4 + $n; # skip "Exif\0\0" header if it exists
9470 2 50       7 if ($start > length($buff)) {
9471 0         0 $et->Warn('Invalid EXIF header');
9472 0         0 next;
9473             }
9474 2 50       5 if ($$et{HTML_DUMP}) {
9475 0         0 $et->HDump($pos, 4, 'Exif header length', "Value: $n");
9476 0 0       0 $et->HDump($pos+4, $start-4, 'Exif header') if $n;
9477             }
9478             }
9479 2         6 $subTable = GetTagTable('Image::ExifTool::Exif::Main');
9480 2         6 $proc = \&Image::ExifTool::ProcessTIFF;
9481             } elsif ($name eq 'PreviewImage') {
9482             # take a quick stab at determining the size of the image
9483             # (based on JPEG previews found in Fuji X-H2S HIF images)
9484 0         0 my $type = 'PreviewImage';
9485 0 0       0 if ($buff =~ /^.{556}\xff\xc0\0\x11.(.{4})/s) {
9486 0         0 my ($h, $w) = unpack('n2', $1);
9487             # (not sure if $h is ever the long dimension, but test it just in case)
9488 0 0 0     0 if ($w == 160 or $h == 160) {
    0 0        
9489 0         0 $type = 'ThumbnailImage';
9490             } elsif ($w == 1920 or $h == 1920) {
9491 0         0 $type = 'OtherImage'; # (large preview)
9492             } # (PreviewImage is 640x480)
9493             }
9494 0         0 $et->FoundTag($type => $buff);
9495 0         0 next;
9496             } elsif ($name eq 'PLIST') {
9497             # extract PLIST information from 'uri ' if available
9498 0 0       0 next unless $buff =~ /^bplist00/;
9499 0         0 $subTable = GetTagTable('Image::ExifTool::PLIST::Main');
9500             } else {
9501 2         8 $subTable = GetTagTable('Image::ExifTool::XMP::Main');
9502             }
9503 4 100       10 $start or $start = 0;
9504 4         23 my %dirInfo = (
9505             DataPt => \$buff,
9506             DataLen => length $buff,
9507             DirStart => $start,
9508             DirLen => length($buff) - $start,
9509             DataPos => $pos,
9510             Base => $pos + $start, # (needed for HtmlDump and IsOffset tags in binary data)
9511             );
9512             # handle processing of metadata for sub-documents
9513 4 50 33     27 if (defined $primary and $$item{RefersTo} and not $$item{RefersTo}{$primary}) {
      33        
9514             # set document number if this doesn't refer to the primary document
9515 0         0 $$et{DOC_NUM} = ++$$et{DOC_COUNT};
9516             # associate this document number with the lowest item index
9517 0         0 my ($lowest) = sort { $a <=> $b } keys %{$$item{RefersTo}};
  0         0  
  0         0  
9518 0         0 $$items{$lowest}{DocNum} = $$et{DOC_NUM};
9519             }
9520 4         29 $et->ProcessDirectory(\%dirInfo, $subTable, $proc);
9521 4         20 delete $$et{DOC_NUM};
9522             }
9523 3 50       12 $raf->Seek($curPos, 0) or $et->Warn('Seek error'); # seek back to original position
9524 3         5 pop @{$$et{PATH}};
  3         25  
9525             }
9526             # process the item properties now that we should know their associations and document numbers
9527 62 100       156 if ($$et{ItemPropertyContainer}) {
9528 3         4 my ($dirInfo, $subTable, $proc) = @{$$et{ItemPropertyContainer}};
  3         9  
9529 3         8 $$et{IsItemProperty} = 1; # set item property flag
9530 3         11 $et->ProcessDirectory($dirInfo, $subTable, $proc);
9531 3         9 delete $$et{ItemPropertyContainer};
9532 3         7 delete $$et{IsItemProperty};
9533 3         12 delete $$et{DOC_NUM};
9534             }
9535 62         94 delete $$et{ItemInfo};
9536 62         147 delete $$et{MediaDataInfo};
9537             }
9538              
9539             #------------------------------------------------------------------------------
9540             # Warn if ExtractEmbedded option isn't used
9541             # Inputs: 0) ExifTool ref
9542             sub EEWarn($)
9543             {
9544 0     0 0 0 my $et = shift;
9545 0         0 $et->Warn('The ExtractEmbedded option may find more tags in the media data',3);
9546             }
9547              
9548             #------------------------------------------------------------------------------
9549             # Get quicktime format from flags word
9550             # Inputs: 0) quicktime atom flags, 1) data length
9551             # Returns: ExifTool format string
9552             sub QuickTimeFormat($$)
9553             {
9554 13     13 0 23 my ($flags, $len) = @_;
9555 13         23 my $format;
9556 13 50 33     76 if ($flags == 0x15 or $flags == 0x16) {
    50          
    50          
    50          
9557 0         0 $format = { 1=>'int8', 2=>'int16', 4=>'int32', 8=>'int64' }->{$len};
9558 0 0       0 $format .= $flags == 0x15 ? 's' : 'u' if $format;
    0          
9559             } elsif ($flags == 0x17) {
9560 0         0 $format = 'float';
9561             } elsif ($flags == 0x18) {
9562 0         0 $format = 'double';
9563             } elsif ($flags == 0x00) {
9564 0         0 $format = { 1=>'int8u', 2=>'int16u' }->{$len};
9565             }
9566 13         22 return $format;
9567             }
9568              
9569             #------------------------------------------------------------------------------
9570             # Process MPEG-4 MTDT atom (ref 11)
9571             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9572             # Returns: 1 on success
9573             sub ProcessMetaData($$$)
9574             {
9575 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
9576 0         0 my $dataPt = $$dirInfo{DataPt};
9577 0         0 my $dirLen = length $$dataPt;
9578 0         0 my $verbose = $et->Options('Verbose');
9579 0 0       0 return 0 unless $dirLen >= 2;
9580 0         0 my $count = Get16u($dataPt, 0);
9581 0 0       0 $verbose and $et->VerboseDir('MetaData', $count);
9582 0         0 my $i;
9583 0         0 my $pos = 2;
9584 0         0 for ($i=0; $i<$count; ++$i) {
9585 0 0       0 last if $pos + 10 > $dirLen;
9586 0         0 my $size = Get16u($dataPt, $pos);
9587 0 0 0     0 last if $size < 10 or $size + $pos > $dirLen;
9588 0         0 my $tag = Get32u($dataPt, $pos + 2);
9589 0         0 my $lang = Get16u($dataPt, $pos + 6);
9590 0         0 my $enc = Get16u($dataPt, $pos + 8);
9591 0         0 my $val = substr($$dataPt, $pos + 10, $size);
9592 0         0 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
9593 0 0       0 if ($tagInfo) {
9594             # convert language code to ASCII (ignore read-only bit)
9595 0         0 $lang = UnpackLang($lang);
9596             # handle alternate languages
9597 0 0       0 if ($lang) {
9598 0         0 my $langInfo = GetLangInfoQT($et, $tagInfo, $lang);
9599 0 0       0 $tagInfo = $langInfo if $langInfo;
9600             }
9601 0 0       0 $verbose and $et->VerboseInfo($tag, $tagInfo,
9602             Value => $val,
9603             DataPt => $dataPt,
9604             Start => $pos + 10,
9605             Size => $size - 10,
9606             );
9607             # convert from UTF-16 BE if necessary
9608 0 0       0 $val = $et->Decode($val, 'UTF16') if $enc == 1;
9609 0 0 0     0 if ($enc == 0 and $$tagInfo{Unknown}) {
9610             # binary data
9611 0         0 $et->FoundTag($tagInfo, \$val);
9612             } else {
9613 0         0 $et->FoundTag($tagInfo, $val);
9614             }
9615             }
9616 0         0 $pos += $size;
9617             }
9618 0         0 return 1;
9619             }
9620              
9621             #------------------------------------------------------------------------------
9622             # Process sample description table
9623             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9624             # Returns: 1 on success
9625             # (ref https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25691)
9626             sub ProcessSampleDesc($$$)
9627             {
9628 48     48 0 116 my ($et, $dirInfo, $tagTablePtr) = @_;
9629 48         94 my $dataPt = $$dirInfo{DataPt};
9630 48   50     177 my $pos = $$dirInfo{DirStart} || 0;
9631 48   33     133 my $dirLen = $$dirInfo{DirLen} || (length($$dataPt) - $pos);
9632 48 50       139 return 0 if $pos + 8 > $dirLen;
9633              
9634 48         132 my $num = Get32u($dataPt, 4); # get number of sample entries in table
9635 48         80 $pos += 8;
9636 48         87 my ($i, $err);
9637 48         120 for ($i=0; $i<$num; ++$i) { # loop through sample entries
9638 48 50       117 $pos + 8 > $dirLen and $err = 1, last;
9639 48         104 my $size = Get32u($dataPt, $pos);
9640 48 50       110 $pos + $size > $dirLen and $err = 1, last;
9641 48         103 $$dirInfo{DirStart} = $pos;
9642 48         81 $$dirInfo{DirLen} = $size;
9643 48         219 ProcessHybrid($et, $dirInfo, $tagTablePtr);
9644 48         126 $pos += $size;
9645             }
9646 48 0 33     118 if ($err and $$et{HandlerType}) {
9647 0   0     0 my $grp = $$et{SET_GROUP1} || $$dirInfo{Parent} || 'unknown';
9648 0         0 $et->Warn("Truncated $$et{HandlerType} sample table for $grp");
9649             }
9650 48         137 return 1;
9651             }
9652              
9653             #------------------------------------------------------------------------------
9654             # Process hybrid binary data + QuickTime container (ref PH)
9655             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9656             # Returns: 1 on success
9657             sub ProcessHybrid($$$)
9658             {
9659 48     48 0 85 my ($et, $dirInfo, $tagTablePtr) = @_;
9660             # brute-force search for child atoms after first 8 bytes of binary data
9661 48         92 my $dataPt = $$dirInfo{DataPt};
9662 48   50     145 my $dirStart = $$dirInfo{DirStart} || 0;
9663 48   33     123 my $dirLen = $$dirInfo{DirLen} || length($$dataPt) - $dirStart;
9664 48         70 my $end = $dirStart + $dirLen;
9665 48         64 my $pos = $dirStart + 8; # skip length/version
9666 48         62 my $try = $pos;
9667 48         78 my $childPos;
9668              
9669 48         120 while ($pos <= $end - 8) {
9670 2664         2916 my $tag = substr($$dataPt, $try+4, 4);
9671             # look only for well-behaved tag ID's
9672 2664 100       4869 $tag =~ /[^\w ]/ and $try = ++$pos, next;
9673 124         213 my $size = Get32u($dataPt, $try);
9674 124 100       211 if ($size + $try == $end) {
9675             # the atom ends exactly at the end of the parent -- this must be it
9676 16         27 $childPos = $pos;
9677 16         29 $$dirInfo{DirLen} = $pos; # the binary data ends at the first child atom
9678 16         29 last;
9679             }
9680 108 100 100     274 if ($size < 8 or $size + $try > $end - 8) {
9681 88         118 $try = ++$pos; # fail. try next position
9682             } else {
9683 20         37 $try += $size; # could be another atom following this
9684             }
9685             }
9686             # process binary data
9687 48         237 $$dirInfo{MixedTags} = 1; # ignore non-integer tag ID's
9688 48         191 $et->ProcessBinaryData($dirInfo, $tagTablePtr);
9689             # process child atoms if found
9690 48 100       117 if ($childPos) {
9691 16         38 $$dirInfo{DirStart} = $childPos;
9692 16         35 $$dirInfo{DirLen} = $end - $childPos;
9693 16         56 ProcessMOV($et, $dirInfo, $tagTablePtr);
9694             }
9695 48         94 return 1;
9696             }
9697              
9698             #------------------------------------------------------------------------------
9699             # Process iTunes 'righ' atom (ref PH)
9700             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9701             # Returns: 1 on success
9702             sub ProcessRights($$$)
9703             {
9704 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
9705 0         0 my $dataPt = $$dirInfo{DataPt};
9706 0         0 my $dataPos = $$dirInfo{Base};
9707 0         0 my $dirLen = length $$dataPt;
9708 0   0     0 my $unknown = $$et{OPTIONS}{Unknown} || $$et{OPTIONS}{Verbose};
9709 0         0 my $pos;
9710 0         0 $et->VerboseDir('righ', $dirLen / 8);
9711 0         0 for ($pos = 0; $pos + 8 <= $dirLen; $pos += 8) {
9712 0         0 my $tag = substr($$dataPt, $pos, 4);
9713 0 0       0 last if $tag eq "\0\0\0\0";
9714 0         0 my $val = substr($$dataPt, $pos + 4, 4);
9715 0         0 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
9716 0 0       0 unless ($tagInfo) {
9717 0 0       0 next unless $unknown;
9718 0         0 my $name = PrintableTagID($tag);
9719 0         0 $tagInfo = {
9720             Name => "Unknown_$name",
9721             Description => "Unknown $name",
9722             Unknown => 1,
9723             },
9724             AddTagToTable($tagTablePtr, $tag, $tagInfo);
9725             }
9726 0 0       0 $val = '0x' . unpack('H*', $val) unless $$tagInfo{Format};
9727 0         0 $et->HandleTag($tagTablePtr, $tag, $val,
9728             DataPt => $dataPt,
9729             DataPos => $dataPos,
9730             Start => $pos + 4,
9731             Size => 4,
9732             );
9733             }
9734 0         0 return 1;
9735             }
9736              
9737             #------------------------------------------------------------------------------
9738             # Process Nextbase 'infi' atom (ref PH)
9739             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9740             # Returns: 1 on success
9741             sub ProcessNextbase($$$)
9742             {
9743 0     0 0 0 my ($et, $dirInfo, $tagTbl) = @_;
9744 0         0 my $dataPt = $$dirInfo{DataPt};
9745 0         0 $et->VerboseDir('Nextbase', undef, length($$dataPt));
9746 0         0 while ($$dataPt =~ /(.*?): +(.*)\x0d/g) {
9747 0         0 my ($id, $val) = ($1, $2);
9748 0 0       0 $$tagTbl{$id} or AddTagToTable($tagTbl, $id, { Name => Image::ExifTool::MakeTagName($id) });
9749 0         0 $et->HandleTag($tagTbl, $id, $val, Size => length($val));
9750             }
9751 0         0 return 1;
9752             }
9753              
9754             #------------------------------------------------------------------------------
9755             # Process iTunes Encoding Params (ref PH)
9756             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9757             # Returns: 1 on success
9758             sub ProcessEncodingParams($$$)
9759             {
9760 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
9761 0         0 my $dataPt = $$dirInfo{DataPt};
9762 0         0 my $dirLen = length $$dataPt;
9763 0         0 my $pos;
9764 0         0 $et->VerboseDir('Encoding Params', $dirLen / 8);
9765 0         0 for ($pos = 0; $pos + 8 <= $dirLen; $pos += 8) {
9766 0         0 my ($tag, $val) = unpack("x${pos}a4N", $$dataPt);
9767 0         0 $et->HandleTag($tagTablePtr, $tag, $val);
9768             }
9769 0         0 return 1;
9770             }
9771              
9772             #------------------------------------------------------------------------------
9773             # Read Meta Keys and add tags to ItemList table ('mdta' handler) (ref PH)
9774             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9775             # Returns: 1 on success
9776             sub ProcessKeys($$$)
9777             {
9778 17     17 0 24 local $_;
9779 17         32 my ($et, $dirInfo, $tagTablePtr) = @_;
9780 17         29 my $dataPt = $$dirInfo{DataPt};
9781 17         32 my $dirLen = length $$dataPt;
9782 17         22 my $out;
9783 17 50       57 if ($et->Options('Verbose')) {
9784 0         0 $et->VerboseDir('Keys');
9785 0         0 $out = $et->Options('TextOut');
9786             }
9787 17         28 my $pos = 8;
9788 17         19 my $index = 1;
9789 17         30 ++$$et{KeysCount}; # increment key count for this directory
9790 17         34 my $itemList = GetTagTable('Image::ExifTool::QuickTime::ItemList');
9791 17         35 my $userData = GetTagTable('Image::ExifTool::QuickTime::UserData');
9792 17         50 while ($pos < $dirLen - 4) {
9793 38         91 my $len = unpack("x${pos}N", $$dataPt);
9794 38 50 33     121 last if $len < 8 or $pos + $len > $dirLen;
9795 38         50 delete $$tagTablePtr{$index};
9796 38         75 my $ns = substr($$dataPt, $pos + 4, 4);
9797 38         61 my $tag = substr($$dataPt, $pos + 8, $len - 8);
9798 38         64 $tag =~ s/\0.*//s; # truncate at null
9799 38         43 my $full = $tag;
9800 38 50       214 $tag =~ s/^com\.(apple\.quicktime\.)?// if $ns eq 'mdta'; # remove apple quicktime domain
9801 38 50       87 $tag = "Tag_$ns" unless $tag;
9802 38         46 my $short = $tag;
9803 38         40 my $tagInfo;
9804 38         37 for (;;) {
9805 38 50       81 $tagInfo = $et->GetTagInfo($tagTablePtr, $tag) and last;
9806             # also try ItemList and UserData tables
9807 0 0       0 $tagInfo = $et->GetTagInfo($itemList, $tag) and last;
9808 0 0       0 $tagInfo = $et->GetTagInfo($userData, $tag) and last;
9809             # (I have some samples where the tag is a reversed ItemList or UserData tag ID)
9810 0 0       0 if ($tag =~ /^\w{3}\xa9$/) {
9811 0         0 $tag = pack('N', unpack('V', $tag));
9812 0 0       0 $tagInfo = $et->GetTagInfo($itemList, $tag) and last;
9813 0         0 $tagInfo = $et->GetTagInfo($userData, $tag);
9814 0         0 last;
9815             }
9816 0 0       0 if ($tag eq $full) {
9817 0         0 $tag = $short;
9818 0         0 last;
9819             }
9820 0         0 $tag = $full;
9821             }
9822 38         52 my ($newInfo, $msg);
9823 38 50 0     53 if ($tagInfo) {
    0          
9824             # copy tag information into new Keys tag
9825             $newInfo = {
9826             Name => $$tagInfo{Name},
9827             Format => $$tagInfo{Format},
9828             ValueConv => $$tagInfo{ValueConv},
9829             ValueConvInv => $$tagInfo{ValueConvInv},
9830             PrintConv => $$tagInfo{PrintConv},
9831             PrintConvInv => $$tagInfo{PrintConvInv},
9832             Writable => defined $$tagInfo{Writable} ? $$tagInfo{Writable} : 1,
9833             SubDirectory => $$tagInfo{SubDirectory},
9834 38 50       280 };
9835 38         62 my $groups = $$tagInfo{Groups};
9836 38 100       165 $$newInfo{Groups} = $groups ? { %$groups } : { };
9837 38   66     242 $$newInfo{Groups}{$_} or $$newInfo{Groups}{$_} = $$tagTablePtr{GROUPS}{$_} foreach 0..2;
9838             # set Keys group. This is necessary for logic when reading the associated ItemList entry,
9839             # but note that the group name will be overridden by TAG_EXTRA G1 for tags in a track
9840 38         66 $$newInfo{Groups}{1} = 'Keys';
9841             } elsif ($tag =~ /^[-\w. ]+$/ or $tag =~ /\w{4}/) {
9842             # create info for tags with reasonable id's
9843 0         0 my $name = ucfirst $tag;
9844 0         0 $name =~ tr/-0-9a-zA-Z_. //dc;
9845 0         0 $name =~ s/[. ]+(.?)/\U$1/g;
9846 0         0 $name =~ s/_([a-z])/_\U$1/g;
9847 0         0 $name =~ s/([a-z])_([A-Z])/$1$2/g;
9848 0 0       0 $name = "Tag_$name" if length $name < 2;
9849 0         0 $newInfo = { Name => $name, Groups => { 1 => 'Keys' } };
9850 0         0 $msg = ' (Unknown)';
9851 0         0 $et->VPrint(0, $$et{INDENT}, "[adding Keys:$tag]\n");
9852             }
9853             # substitute this tag in the ItemList table with the given index
9854 38         81 my $id = $$et{KeysCount} . '.' . $index;
9855 38 100       105 if (ref $$itemList{$id} eq 'HASH') {
9856             # delete other languages too if they exist
9857 30         57 my $oldInfo = $$itemList{$id};
9858 30 100       83 if ($$oldInfo{OtherLang}) {
9859 1         2 delete $$itemList{$_} foreach @{$$oldInfo{OtherLang}};
  1         7  
9860             }
9861 30         189 delete $$itemList{$id};
9862             }
9863 38 50       60 if ($newInfo) {
9864 38         71 $$newInfo{KeysID} = $tag; # save original ID for use in family 7 group name
9865 38         89 AddTagToTable($itemList, $id, $newInfo);
9866 38 50       65 $msg or $msg = '';
9867 38 50       67 $out and print $out "$$et{INDENT}Added ItemList Tag $id = ($ns) $full$msg\n";
9868             }
9869 38         52 $pos += $len;
9870 38         97 ++$index;
9871             }
9872 17         38 return 1;
9873             }
9874              
9875             #------------------------------------------------------------------------------
9876             # Process keys in MetaSampleDesc directory
9877             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9878             # Returns: 1 on success
9879             sub ProcessMetaKeys($$$)
9880             {
9881 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
9882             # save this information to decode timed metadata samples when ExtractEmbedded is used
9883 0 0       0 SaveMetaKeys($et, $dirInfo, $tagTablePtr) if $$et{OPTIONS}{ExtractEmbedded};
9884 0         0 return 1;
9885             }
9886              
9887             #------------------------------------------------------------------------------
9888             # Identify trailers at specified offset from end of file
9889             # Inputs: 0) RAF reference, 1) Offset from end of file
9890             # Returns: Array ref to first trailer in linked list: 0) name of trailer,
9891             # 1) absolute offset to start of this trailer, 2) trailer length,
9892             # 3) ref to next trailer. Or undef if no trailer found, or error string on error
9893             # - file position is returned to its original location
9894             sub IdentifyTrailers($)
9895             {
9896 48     48 0 74 my $raf = shift;
9897 48         100 my ($trailer, $nextTrail, $buff, $type, $len);
9898 48         124 my $pos = $raf->Tell();
9899 48         97 my $offset = 0; # positive offset back from end of file
9900 48   33     155 while ($raf->Seek(-40-$offset, 2) and $raf->Read($buff, 40) == 40) {
9901 48 50 33     484 if (substr($buff, 8) eq '8db42d694ccc418790edff439fe026bf') {
    50          
    50          
9902 0         0 ($type, $len) = ('Insta360', unpack('V',$buff));
9903             } elsif ($buff =~ /\&\&\&\&(.{4})$/) {
9904 0         0 ($type, $len) = ('LigoGPS', Get32u(\$buff, 36));
9905             } elsif ($buff =~ /~\0\x04\0zmie~\0\0\x06.{4}([\x10\x18])(\x04)$/s or
9906             $buff =~ /~\0\x04\0zmie~\0\0\x0a.{8}([\x10\x18])(\x08)$/s)
9907             {
9908 0         0 my $oldOrder = GetByteOrder();
9909 0 0       0 SetByteOrder($1 eq "\x10" ? 'MM' : 'II');
9910 0         0 $type = 'MIE';
9911 0 0       0 $len = ($2 eq "\x04") ? Get32u(\$buff, 34) : Get64u(\$buff, 30);
9912 0         0 SetByteOrder($oldOrder);
9913             } else {
9914 48         83 last;
9915             }
9916             # 0) trailer type, 1) trailer start, 2) trailer length, 3) pointer to next trailer
9917 0         0 $trailer = [ $type , $raf->Tell() - $len, $len, $nextTrail ];
9918 0         0 $nextTrail = $trailer;
9919 0         0 $offset += $len;
9920             }
9921 48 50       136 $raf->Seek($pos,0) or return 'Seek error';
9922 48         101 return $trailer;
9923             }
9924              
9925             #------------------------------------------------------------------------------
9926             # Process a QuickTime atom
9927             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) optional tag table ref
9928             # Returns: 1 on success
9929             sub ProcessMOV($$;$)
9930             {
9931 486     486 0 746 local $_;
9932 486         784 my ($et, $dirInfo, $tagTablePtr) = @_;
9933 486         724 my $raf = $$dirInfo{RAF};
9934 486         682 my $dataPt = $$dirInfo{DataPt};
9935 486         1226 my $verbose = $et->Options('Verbose');
9936 486         874 my $validate = $$et{OPTIONS}{Validate};
9937 486   100     1091 my $dirBase = $$dirInfo{Base} || 0;
9938 486         624 my $dataPos = $dirBase;
9939 486   100     1054 my $dirID = $$dirInfo{DirID} || '';
9940 486         764 my $charsetQuickTime = $et->Options('CharsetQuickTime');
9941 486         1080 my ($buff, $tag, $size, $track, $isUserData, %triplet, $doDefaultLang, $index);
9942 486         0 my ($dirEnd, $unkOpt, %saveOptions, $atomCount, $warnStr, $trailer);
9943              
9944 486         721 my $topLevel = not $$et{InQuickTime};
9945 486         640 $$et{InQuickTime} = 1;
9946 486 100       855 $$et{HandlerType} = $$et{MetaFormat} = $$et{MediaType} = '' if $topLevel;
9947              
9948 486 100       1025 unless (defined $$et{KeysCount}) {
9949 25         50 $$et{KeysCount} = 0; # initialize ItemList key directory count
9950 25         48 $doDefaultLang = 1; # flag to generate default language tags
9951             }
9952             # more convenient to package data as a RandomAccess file
9953 486 100       897 unless ($raf) {
9954 461         1349 $raf = File::RandomAccess->new($dataPt);
9955 461 50 100     1715 $dirEnd = $dataPos + $$dirInfo{DirLen} + ($$dirInfo{DirStart} || 0) if $$dirInfo{DirLen};
9956             }
9957             # skip leading bytes if necessary
9958 486 100       909 if ($$dirInfo{DirStart}) {
9959 108 50       341 $raf->Seek($$dirInfo{DirStart}, 1) or return 0;
9960 108         188 $dataPos += $$dirInfo{DirStart};
9961             }
9962             # read size/tag name atom header
9963 486 50       894 $raf->Read($buff,8) == 8 or return 0;
9964 486         683 $dataPos += 8;
9965 486 100       813 if ($tagTablePtr) {
9966 461         1040 $isUserData = ($tagTablePtr eq \%Image::ExifTool::QuickTime::UserData);
9967             } else {
9968 25         75 $tagTablePtr = GetTagTable('Image::ExifTool::QuickTime::Main');
9969             }
9970 486         1410 ($size, $tag) = unpack('Na4', $buff);
9971 486   50     1533 my $fast = $$et{OPTIONS}{FastScan} || 0;
9972             # check for Insta360, LIGOGPSINFO or MIE trailer
9973 486 100 66     1058 if ($topLevel and not $fast) {
9974 25         105 $trailer = IdentifyTrailers($raf);
9975 25 50 33     78 $trailer and not ref $trailer and $et->Warn($trailer), return 0;
9976             }
9977 486 100       1006 if ($dataPt) {
9978 461 50       729 $verbose and $et->VerboseDir($$dirInfo{DirName});
9979             } else {
9980             # check on file type if called with a RAF
9981 25 50       107 $$tagTablePtr{$tag} or return 0;
9982 25         44 my $fileType;
9983 25 100 66     97 if ($tag eq 'ftyp' and $size >= 12) {
9984             # read ftyp atom to see what type of file this is
9985 11 50       39 if ($raf->Read($buff, $size-8) == $size-8) {
9986 11 50       47 $raf->Seek(-($size-8), 1) or $et->Warn('Seek error'), return 0;
9987 11         27 my $type = substr($buff, 0, 4);
9988 11         35 $$et{save_ftyp} = $type;
9989             # see if we know the extension for this file type
9990 11 50 33     148 if ($ftypLookup{$type} and $ftypLookup{$type} =~ /\(\.(\w+)/) {
    0          
    0          
    0          
9991 11         39 $fileType = $1;
9992             # check compatible brands
9993             } elsif ($buff =~ /^.{8}(.{4})+(mp41|mp42|avc1)/s) {
9994 0         0 $fileType = 'MP4';
9995             } elsif ($buff =~ /^.{8}(.{4})+(f4v )/s) {
9996 0         0 $fileType = 'F4V';
9997             } elsif ($buff =~ /^.{8}(.{4})+(qt )/s) {
9998 0         0 $fileType = 'MOV';
9999             }
10000             }
10001 11 50       35 $fileType or $fileType = 'MP4'; # default to MP4
10002             # set file type from extension if appropriate
10003 11         28 my $ext = $$et{FILE_EXT};
10004 11 50 33     76 $fileType = $ext if $ext and $useExt{$ext} and $fileType eq $useExt{$ext};
      33        
10005 11   50     92 $et->SetFileType($fileType, $mimeLookup{$fileType} || 'video/mp4');
10006             # temporarily set ExtractEmbedded option for CRX files
10007 11 100       41 $saveOptions{ExtractEmbedded} = $et->Options(ExtractEmbedded => 1) if $fileType eq 'CRX';
10008             } else {
10009 14         69 $et->SetFileType(); # MOV
10010             }
10011 25         102 SetByteOrder('MM');
10012             # have XMP take priority except for HEIC
10013 25 50 66     124 $$et{PRIORITY_DIR} = 'XMP' unless $fileType and $fileType eq 'HEIC';
10014             }
10015 486 50       842 $$raf{NoBuffer} = 1 if $fast; # disable buffering in FastScan mode
10016              
10017 486   100     1143 my $ee = $$et{OPTIONS}{ExtractEmbedded} || 0;
10018 486         789 my $hash = $$et{ImageDataHash};
10019 486 100 66     1237 if ($ee or $hash) {
10020 134         233 $unkOpt = $$et{OPTIONS}{Unknown};
10021 134         3153 require 'Image/ExifTool/QuickTimeStream.pl';
10022             }
10023 486 100       969 if ($$tagTablePtr{VARS}) {
10024 22         49 $index = $$tagTablePtr{VARS}{START_INDEX};
10025 22         38 $atomCount = $$tagTablePtr{VARS}{ATOM_COUNT};
10026             }
10027 486         685 my $lastTag = '';
10028 486         571 my $lastPos = 0;
10029 486         559 for (;;) {
10030 1576         1910 my ($eeTag, $ignore);
10031 1576 50 33     3268 last if defined $atomCount and --$atomCount < 0;
10032 1576 100       2084 if ($size < 8) {
10033 4 50       19 if ($size == 0) {
10034 0 0       0 if ($dataPt) {
10035             # a zero size isn't legal for contained atoms, but Canon uses it to
10036             # terminate the CNTH atom (eg. CanonEOS100D.mov), so tolerate it here
10037 0         0 my $pos = $raf->Tell() - 4;
10038 0 0       0 $raf->Seek(0,2) or $et->Warn('Seek error'), return 0;
10039 0         0 my $str = $$dirInfo{DirName} . ' with ' . ($raf->Tell() - $pos) . ' bytes';
10040 0         0 $et->VPrint(0,"$$et{INDENT}\[Terminator found in $str remaining]");
10041             } else {
10042 0         0 my $t = PrintableTagID($tag,2);
10043 0         0 $et->VPrint(0,"$$et{INDENT}Tag '${t}' extends to end of file");
10044 0 0       0 if ($$tagTablePtr{"$tag-size"}) {
10045 0         0 my $pos = $raf->Tell();
10046 0 0       0 unless ($fast) {
10047 0 0       0 $raf->Seek(0, 2) or $et->Warn('Seek error'), return 0;
10048 0         0 $et->HandleTag($tagTablePtr, "$tag-size", $raf->Tell() - $pos);
10049             }
10050 0 0       0 $et->HandleTag($tagTablePtr, "$tag-offset", $pos) if $$tagTablePtr{"$tag-offset"};
10051             }
10052             }
10053 0         0 last;
10054             }
10055 4 50       13 $size == 1 or $warnStr = 'Invalid atom size', last;
10056             # read extended atom size
10057 4 50       14 $raf->Read($buff, 8) == 8 or $warnStr = 'Truncated atom header', last;
10058 4         8 $dataPos += 8;
10059 4         12 my ($hi, $lo) = unpack('NN', $buff);
10060 4 50 33     21 if ($hi or $lo > 0x7fffffff) {
10061 0 0       0 if ($hi > 0x7fffffff) {
    0          
    0          
10062 0         0 $warnStr = 'Invalid atom size';
10063 0         0 last;
10064             } elsif (not $et->Options('LargeFileSupport')) {
10065 0         0 $warnStr = 'End of processing at large atom (LargeFileSupport not enabled)';
10066 0         0 last;
10067             } elsif ($et->Options('LargeFileSupport') eq '2') {
10068 0         0 $et->Warn('Processing large atom (LargeFileSupport is 2)');
10069             }
10070             }
10071 4         10 $size = $hi * 4294967296 + $lo - 16;
10072 4 50       13 $size < 0 and $warnStr = 'Invalid extended size', last;
10073             } else {
10074 1572         1881 $size -= 8;
10075             }
10076 1576 50       2632 if ($validate) {
10077 0 0 0     0 $et->Warn("Invalid 'wide' atom size") if $tag eq 'wide' and $size;
10078 0 0       0 $$et{ValidatePath} or $$et{ValidatePath} = { };
10079 0         0 my $path = join('-', @{$$et{PATH}}, $tag);
  0         0  
10080 0 0       0 $path =~ s/-Track-/-$$et{SET_GROUP1}-/ if $$et{SET_GROUP1};
10081 0 0 0     0 if ($$et{ValidatePath}{$path} and not $dupTagOK{$tag} and not $dupDirOK{$dirID}) {
      0        
10082 0         0 my $i = Get32u(\$tag,0);
10083 0 0       0 my $str = $i < 255 ? "index $i" : "tag '" . PrintableTagID($tag,2) . "'";
10084 0         0 $path =~ s/-[^-+]$//; # remove tag name
10085 0         0 $et->Warn("Duplicate $str at $path");
10086 0 0       0 $$et{ValidatePath} = { } if $path eq 'MOV-moov'; # avoid warnings for all contained dups
10087             }
10088 0         0 $$et{ValidatePath}{$path} = 1;
10089             }
10090 1576 50 66     2507 if ($isUserData and $$et{SET_GROUP1}) {
10091 0         0 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
10092 0 0 0     0 unless ($tagInfo and $$tagInfo{SubDirectory}) {
10093             # add track name to UserData tags inside tracks
10094 0         0 $tag = $$et{SET_GROUP1} . $tag;
10095 0 0 0     0 if (not $$tagTablePtr{$tag} and $tagInfo) {
10096 0         0 my %newInfo = %$tagInfo;
10097 0         0 foreach ('Name', 'Description') {
10098 0 0       0 next unless $$tagInfo{$_};
10099 0         0 $newInfo{$_} = $$et{SET_GROUP1} . $$tagInfo{$_};
10100 0         0 $newInfo{$_} =~ s/^(Track\d+)Track/$1/; # remove duplicate "Track" in name
10101             }
10102 0         0 AddTagToTable($tagTablePtr, $tag, \%newInfo);
10103             }
10104             }
10105             }
10106             # set flag to store additional information for ExtractEmbedded option
10107 1576         2617 my $handlerType = $$et{HandlerType};
10108 1576 100 100     7263 if ($eeBox{$handlerType} and $eeBox{$handlerType}{$tag}) {
    50 33        
    50 0        
      33        
      0        
10109 124 100 66     342 if ($ee or $hash) {
10110             # (there is another 'gps ' box with a track log that doesn't contain offsets)
10111 68 50 33     161 if ($tag ne 'gps ' or $eeBox{$handlerType}{$tag} eq $dirID) {
10112 68         80 $eeTag = 1;
10113 68         118 $$et{OPTIONS}{Unknown} = 1; # temporarily enable "Unknown" option
10114             }
10115             }
10116             } elsif ($ee > 1 and $eeBox2{$handlerType} and $eeBox2{$handlerType}{$tag}) {
10117 0         0 $eeTag = 1;
10118 0         0 $$et{OPTIONS}{Unknown} = 1;
10119             } elsif ($hash and $hashBox{$handlerType} and $hashBox{$handlerType}{$tag}) {
10120 0         0 $eeTag = 1;
10121 0         0 $$et{OPTIONS}{Unknown} = 1;
10122             }
10123 1576         3716 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
10124              
10125 1576 100       2596 $$et{OPTIONS}{Unknown} = $unkOpt if $eeTag; # restore Unknown option
10126              
10127             # allow numerical tag ID's
10128 1576 100       3162 unless ($tagInfo) {
10129 178         524 my $id = $$et{KeysCount} . '.' . unpack('N', $tag);
10130 178 100       400 if ($$tagTablePtr{$id}) {
10131 37         69 $tagInfo = $et->GetTagInfo($tagTablePtr, $id);
10132 37         53 $tag = $id;
10133             }
10134             }
10135             # generate tagInfo if Unknown option set
10136 1576 50 66     2974 if (not defined $tagInfo and ($$et{OPTIONS}{Unknown} or
      66        
10137             $verbose or $tag =~ /^\xa9/))
10138             {
10139 13         48 my $name = PrintableTagID($tag,1);
10140 13 50       34 if ($name =~ /^xa9(.*)/) {
10141 0         0 $tagInfo = {
10142             Name => "UserData_$1",
10143             Description => "User Data $1",
10144             };
10145             } else {
10146 13         92 $tagInfo = {
10147             Name => "Unknown_$name",
10148             Description => "Unknown $name",
10149             %unknownInfo,
10150             };
10151             }
10152 13         45 AddTagToTable($tagTablePtr, $tag, $tagInfo);
10153             }
10154             # save required tag sizes
10155 1576 100       3816 if ($$tagTablePtr{"$tag-size"}) {
10156 28         130 $et->HandleTag($tagTablePtr, "$tag-size", $size);
10157 28 50       177 $et->HandleTag($tagTablePtr, "$tag-offset", $raf->Tell()+$dirBase) if $$tagTablePtr{"$tag-offset"};
10158             }
10159             # save position/size of 'idat'
10160 1576 50       2499 $$et{MediaDataInfo} = [ $raf->Tell() + $dirBase, $size ] if $tag eq 'idat';
10161             # stop processing at mdat/idat if -fast2 is used
10162 1576 0 0     2274 last if $fast > 1 and ($tag eq 'mdat' or ($tag eq 'idat' and $$et{FileType} ne 'HEIC'));
      33        
10163             # load values only if associated with a tag (or verbose) and not too big
10164 1576 50       2346 if ($size > 0x2000000) { # start to get worried above 32 MiB
10165             # check for RIFF trailer (written by Auto-Vox dashcam)
10166 0 0       0 if ($buff =~ /^(gpsa|gps0|gsen|gsea)...\0/s) { # (yet seen only gpsa as first record)
    0          
10167 0         0 $et->VPrint(0, sprintf("Found RIFF trailer at offset 0x%x",$lastPos));
10168 0 0       0 if ($ee) {
10169 0 0       0 $raf->Seek(-8, 1) or last; # seek back to start of trailer
10170 0         0 my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
10171 0         0 ProcessRIFFTrailer($et, { RAF => $raf }, $tbl);
10172             } else {
10173 0         0 EEWarn($et);
10174             }
10175 0         0 last;
10176             } elsif ($buff eq 'CCCCCCCC') {
10177 0         0 $et->VPrint(0, sprintf("Found Kenwood trailer at offset 0x%x",$lastPos));
10178 0         0 my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
10179 0         0 ProcessKenwoodTrailer($et, { RAF => $raf }, $tbl);
10180 0         0 last;
10181             }
10182 0 0 0     0 if (not $tagInfo or $$tagInfo{Unknown}) {
    0          
    0          
10183 0         0 $ignore = 1;
10184             } elsif ($size > 0x8000000) {
10185 0         0 my $t = PrintableTagID($tag,2);
10186 0 0       0 $et->Warn("Skipping '${t}' atom > 128 MiB", $eeTag ? 2 : 1) and $ignore = 1;
    0          
10187             } elsif (not $eeTag) {
10188 0         0 my $t = PrintableTagID($tag,2);
10189 0 0       0 $et->Warn("Skipping '${t}' atom > 32 MiB", 2) and $ignore = 1;
10190             }
10191             }
10192 1576 100 66     6499 if (defined $tagInfo and not $ignore and not ($tagInfo and $$tagInfo{DontRead})) {
      66        
      66        
10193             # set document number for this item property if necessary
10194 1488 100       2684 if ($$et{IsItemProperty}) {
10195 12         19 my $items = $$et{ItemInfo};
10196 12         15 my ($id, $prop, $docNum, $lowest);
10197 12   50     29 my $primary = $$et{PrimaryItem} || 0;
10198 12   50     29 my $pitem = $$items{$primary} || { };
10199 12 100       27 $$pitem{RefersTo} or $$pitem{RefersTo} = { };
10200 12         55 ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
  44         76  
10201 44 100       85 next unless $$items{$id}{Association};
10202 24         32 my $item = $$items{$id};
10203 24         23 foreach $prop (@{$$item{Association}}) {
  24         37  
10204 45 100       65 next unless $prop == $index;
10205 12   50     29 my $dont = $dontInherit{$tag} || 0;
10206 12 100 0     56 if ($id == $primary or (not $dont and
    100 33        
      66        
      66        
      33        
10207             ($$item{RefersTo} and $$item{RefersTo}{$primary})) or
10208             # special case to assume this property belongs to the primary
10209             # image if it belongs to an item referred to by the primary
10210             ($dont != 1 and $$pitem{RefersTo}{$id}))
10211             {
10212             # this is associated with the primary item or an item describing
10213             # the primary item, so consider this part of the main document
10214 6         8 undef $docNum;
10215 6         9 undef $lowest;
10216 6         14 last ItemID;
10217             } elsif ($$item{DocNum}) {
10218             # this property is already associated with an item that has
10219             # an ExifTool document number, so use the lowest associated DocNum
10220 3 50 33     16 $docNum = $$item{DocNum} if not defined $docNum or $docNum > $$item{DocNum};
10221             } else {
10222             # keep track of the lowest associated item ID
10223 3         5 $lowest = $id;
10224             }
10225             }
10226             }
10227 12 100 100     35 if (not defined $docNum and defined $lowest) {
10228             # this is the first time we've seen metadata from this item,
10229             # so use a new document number
10230 3         7 $docNum = ++$$et{DOC_COUNT};
10231 3         8 $$items{$lowest}{DocNum} = $docNum;
10232             }
10233 12         17 $$et{DOC_NUM} = $docNum;
10234             }
10235 1488         1587 my $val;
10236 1488         2884 my $missing = $size - $raf->Read($val, $size);
10237 1488 50       2389 if ($missing) {
10238 0         0 my $t = PrintableTagID($tag,2);
10239 0         0 $warnStr = "Truncated '${t}' data (missing $missing bytes)";
10240 0         0 last;
10241             }
10242             # use value to get tag info if necessary
10243 1488 100       2347 $tagInfo or $tagInfo = $et->GetTagInfo($tagTablePtr, $tag, \$val);
10244 1488   100     3282 my $hasData = ($$dirInfo{HasData} and $val =~ /^....data\0/s);
10245 1488 0 33     2463 if ($verbose and defined $val and not $hasData) {
      33        
10246 0         0 my $tval;
10247 0 0 0     0 if ($tagInfo and $$tagInfo{Format}) {
10248 0         0 $tval = ReadValue(\$val, 0, $$tagInfo{Format}, $$tagInfo{Count}, length($val));
10249             }
10250             $et->VerboseInfo($tag, $tagInfo,
10251             Value => $tval,
10252             DataPt => \$val,
10253             DataPos => $dataPos,
10254             Size => $size,
10255             Format => $tagInfo ? $$tagInfo{Format} : undef,
10256 0 0       0 Index => $index,
10257             );
10258             # print iref item ID numbers
10259 0 0       0 if ($dirID eq 'iref') {
10260 0         0 my ($id, $count, @to, $i);
10261 0 0       0 if ($$et{ItemRefVersion}) {
10262 0 0       0 ($id, $count, @to) = unpack('NnN*', $val) if length $val >= 10;
10263             } else {
10264 0 0       0 ($id, $count, @to) = unpack('nnn*', $val) if length $val >= 6;
10265             }
10266 0 0       0 defined $id or $id = '', $count = 0;
10267 0 0       0 $id .= " (wrong count: $count)" if $count != @to;
10268             # convert sequential numbers to a range
10269 0         0 for ($i=1; $i<@to; ) {
10270 0 0 0     0 $to[$i-1] =~ /(\d+)$/ and $to[$i] == $1 + 1 or ++$i, next;
10271 0         0 $to[$i-1] =~ s/(-.*)?$/-$to[$i]/;
10272 0         0 splice @to, $i, 1;
10273             }
10274 0         0 $et->VPrint(1, "$$et{INDENT} Item $id refers to: ",join(',',@to),"\n");
10275             }
10276             }
10277             # extract metadata from stream if ExtractEmbedded option is enabled
10278 1488 100       2269 if ($eeTag) {
10279 68         224 ParseTag($et, $tag, \$val);
10280             # forget this tag if we generated it only for ExtractEmbedded
10281 68 100 66     338 undef $tagInfo if $tagInfo and $$tagInfo{Unknown} and not $unkOpt;
      100        
10282             }
10283              
10284             # handle iTunesInfo mean/name/data triplets
10285 1488 100 100     3806 if ($tagInfo and $$tagInfo{Triplet}) {
10286 9 100 66     32 if ($tag eq 'data' and $triplet{mean} and $triplet{name}) {
      33        
10287 3         5 $tag = $triplet{name};
10288             # add 'mean' to name unless it is 'com.apple.iTunes'
10289 3 50       9 $tag = $triplet{mean} . '/' . $tag unless $triplet{mean} eq 'com.apple.iTunes';
10290 3         10 $tagInfo = $et->GetTagInfo($tagTablePtr, $tag, \$val);
10291 3 50       7 unless ($tagInfo) {
10292 0         0 my $name = $triplet{name};
10293 0         0 my $desc = $name;
10294 0         0 $name =~ tr/-_a-zA-Z0-9//dc;
10295 0         0 $desc =~ tr/_/ /;
10296 0         0 $tagInfo = {
10297             Name => $name,
10298             Description => $desc,
10299             };
10300 0         0 $et->VPrint(0, $$et{INDENT}, "[adding QuickTime:$name]\n");
10301 0         0 AddTagToTable($tagTablePtr, $tag, $tagInfo);
10302             }
10303             # ignore 8-byte header
10304 3 50       11 $val = substr($val, 8) if length($val) >= 8;
10305 3 50 33     13 unless ($$tagInfo{Format} or $$tagInfo{SubDirectory}) {
10306             # extract as binary if it contains any non-ASCII or control characters
10307 3 50       13 if ($val =~ /[^\x20-\x7e]/) {
10308 0         0 my $buff = $val;
10309 0         0 $val = \$buff;
10310             }
10311             }
10312 3         6 $$tagInfo{List} = 1; # (allow any of these tags to have multiple data elements)
10313 3 50       5 $et->VerboseInfo($tag, $tagInfo, Value => $val) if $verbose;
10314             } else {
10315 6 50       21 $triplet{$tag} = substr($val,4) if length($val) > 4;
10316 6         8 undef $tagInfo; # don't store this tag
10317             }
10318             }
10319 1488 100       2242 if ($tagInfo) {
10320 1454         1595 my @found;
10321 1454         1878 my $subdir = $$tagInfo{SubDirectory};
10322 1454 100 66     3212 if ($subdir) {
    100          
    100          
10323 863   100     2212 my $start = $$subdir{Start} || 0;
10324 863         1411 my ($base, $dPos) = ($dataPos, 0);
10325 863 50       1405 if ($$subdir{Base}) {
10326 0         0 $dPos -= eval $$subdir{Base};
10327 0         0 $base -= $dPos;
10328             }
10329             my %dirInfo = (
10330             DataPt => \$val,
10331             DataLen => $size,
10332             DirStart => $start,
10333             DirLen => $size - $start,
10334             DirName => $$subdir{DirName} || $$tagInfo{Name},
10335             DirID => $tag,
10336             HasData => $$subdir{HasData},
10337             Multi => $$subdir{Multi},
10338             IgnoreProp => $$subdir{IgnoreProp}, # (XML hack)
10339 863   66     6746 DataPos => $dPos,
10340             Base => $base, # (needed for IsOffset tags in binary data)
10341             );
10342 863 50       1809 $dirInfo{BlockInfo} = $tagInfo if $$tagInfo{BlockExtract};
10343 863 100 66     1800 if ($$subdir{ByteOrder} and $$subdir{ByteOrder} =~ /^Little/) {
10344 9         25 SetByteOrder('II');
10345             }
10346 863         1343 my $oldGroup1 = $$et{SET_GROUP1};
10347 863 100 33     3617 if ($$tagInfo{SubDirectory} and $$tagInfo{SubDirectory}{TagTable} and
      66        
10348             $$tagInfo{SubDirectory}{TagTable} eq 'Image::ExifTool::QuickTime::Track')
10349             {
10350 48 100       106 $track or $track = 0;
10351 48         133 $$et{SET_GROUP1} = 'Track' . (++$track);
10352             }
10353 863         2074 my $subTable = GetTagTable($$subdir{TagTable});
10354 863         1323 my $proc = $$subdir{ProcessProc};
10355             # make ProcessMOV() the default processing procedure for subdirectories
10356 863 100 100     2742 $proc = \&ProcessMOV unless $proc or $$subTable{PROCESS_PROC};
10357 863 50       1458 if ($size > $start) {
10358             # delay processing of ipco box until after all other boxes
10359 863 100 66     2339 if ($tag eq 'ipco' and not $$et{IsItemProperty}) {
    50 33        
10360 3         12 $$et{ItemPropertyContainer} = [ \%dirInfo, $subTable, $proc ];
10361 3         17 $et->VPrint(0,"$$et{INDENT}\[Process ipco box later]");
10362             } elsif ($fast < 2 or not $$tagInfo{MakerNotes}) {
10363 860         2181 $et->ProcessDirectory(\%dirInfo, $subTable, $proc);
10364             }
10365             }
10366 863 100       2092 if ($tag eq 'stbl') {
    100          
10367             # process sample data when exiting SampleTable box if extracting embedded
10368 48 100 66     259 ProcessSamples($et) if $ee or $hash;
10369             } elsif ($tag eq 'minf') {
10370 48         116 $$et{HandlerType} = ''; # reset handler type at end of media info box
10371             }
10372 863         1598 $$et{SET_GROUP1} = $oldGroup1;
10373 863         1496 SetByteOrder('MM');
10374             } elsif ($hasData) {
10375             # handle atoms containing 'data' tags
10376             # (currently ignore contained atoms: 'itif', 'name', etc.)
10377 189         234 my $pos = 0;
10378 189         179 for (;;) {
10379 382 100       651 last if $pos + 16 > $size;
10380 193         518 my ($len, $type, $flags, $ctry, $lang) = unpack("x${pos}Na4Nnn", $val);
10381 193 50 33     498 last if $pos + $len > $size or not $len;
10382 193         253 my ($value, $langInfo, $oldDir);
10383 193         259 my $format = $$tagInfo{Format};
10384 193 50 33     436 if ($type eq 'data' and $len >= 16) {
10385 193         205 $pos += 16;
10386 193         202 $len -= 16;
10387 193         274 $value = substr($val, $pos, $len);
10388             # format flags (ref 12):
10389             # 0x0=binary, 0x1=UTF-8, 0x2=UTF-16, 0x3=ShiftJIS,
10390             # 0x4=UTF-8 0x5=UTF-16, 0xd=JPEG, 0xe=PNG,
10391             # 0x15=signed int, 0x16=unsigned int, 0x17=float,
10392             # 0x18=double, 0x1b=BMP, 0x1c='meta' atom
10393 193 100       396 if ($stringEncoding{$flags}) {
10394             # handle all string formats
10395 144         376 $value = $et->Decode($value, $stringEncoding{$flags});
10396             # (shouldn't be null terminated, but some software writes it anyway)
10397 144 50       328 $value =~ s/\0$// unless $$tagInfo{Binary};
10398             } else {
10399 49 100       149 if (not $format) {
    100          
10400 13         46 $format = QuickTimeFormat($flags, $len);
10401             } elsif ($format =~ /^int\d+([us])$/) {
10402             # adjust integer to available length (but not int64)
10403 16         71 my $fmt = { 1=>'int8', 2=>'int16', 4=>'int32' }->{$len};
10404 16 50       77 $format = $fmt . $1 if defined $fmt;
10405             }
10406 49 100       101 if ($format) {
    50          
10407 36         152 $value = ReadValue(\$value, 0, $format, $$tagInfo{Count}, $len);
10408             } elsif (not $$tagInfo{ValueConv}) {
10409             # make binary data a scalar reference unless a ValueConv exists
10410 13         19 my $buf = $value;
10411 13         24 $value = \$buf;
10412             }
10413             }
10414             }
10415 193 100 66     455 if ($ctry or $lang) {
10416 3         13 my $langCode = GetLangCode($lang, $ctry);
10417 3 50       9 if ($langCode) {
10418             # get tagInfo for other language
10419 3         8 $langInfo = GetLangInfoQT($et, $tagInfo, $langCode);
10420             # save other language tag ID's so we can delete later if necessary
10421 3 50       11 if ($langInfo) {
10422 3 50       11 $$tagInfo{OtherLang} or $$tagInfo{OtherLang} = [ ];
10423 3         6 push @{$$tagInfo{OtherLang}}, $$langInfo{TagID};
  3         7  
10424             }
10425             }
10426             }
10427 193 100       287 $langInfo or $langInfo = $tagInfo;
10428 193 50       480 my $str = $qtFlags{$flags} ? " ($qtFlags{$flags})" : '';
10429 193 0       267 $et->VerboseInfo($tag, $langInfo,
    50          
10430             Value => ref $value ? $$value : $value,
10431             DataPt => \$val,
10432             DataPos => $dataPos,
10433             Start => $pos,
10434             Size => $len,
10435             Format => $format,
10436             Index => $index,
10437             Extra => sprintf(", Type='${type}', Flags=0x%x%s, Lang=0x%.4x",$flags,$str,$lang),
10438             ) if $verbose;
10439 193 50       264 if (defined $value) {
10440             # use "Keys" in path instead of ItemList if this was defined by a Keys tag
10441             # (the only reason for this is to have "Keys" in the family 5 group name)
10442             # Note that the Keys group is specifically set by the ProcessKeys routine,
10443             # even though this tag would be in the ItemList table
10444 193   100     741 my $isKeys = $$tagInfo{Groups} && $$tagInfo{Groups}{1} && $$tagInfo{Groups}{1} eq 'Keys';
10445 193 100       338 $isKeys and $oldDir = $$et{PATH}[-1], $$et{PATH}[-1] = 'Keys';
10446 193         387 push @found, $et->FoundTag($langInfo, $value);
10447 193 100       352 $$et{PATH}[-1] = $oldDir if $isKeys;
10448             }
10449 193         280 $pos += $len;
10450             }
10451             } elsif ($tag =~ /^\xa9/ or $$tagInfo{IText}) {
10452             # parse international text to extract all languages
10453 71         125 my $pos = 0;
10454 71 50       115 if ($$tagInfo{Format}) {
10455 0         0 push @found, $et->FoundTag($tagInfo, ReadValue(\$val, 0, $$tagInfo{Format}, undef, length($val)));
10456 0         0 $pos = $size;
10457             }
10458 71         86 for (;;) {
10459 142         187 my ($len, $lang);
10460 142 100 66     252 if ($$tagInfo{IText} and $$tagInfo{IText} >= 6) {
10461 4 100       11 last if $pos + $$tagInfo{IText} > $size;
10462 2         4 $pos += $$tagInfo{IText} - 2;
10463 2         5 $lang = unpack("x${pos}n", $val);
10464 2         4 $pos += 2;
10465 2         3 $len = $size - $pos;
10466             } else {
10467 138 100       243 last if $pos + 4 > $size;
10468 69         176 ($len, $lang) = unpack("x${pos}nn", $val);
10469 69         86 $pos += 4;
10470             # according to the QuickTime spec (ref 12), $len should include
10471             # 4 bytes for length and type words, but nobody (including
10472             # Apple, Pentax and Kodak) seems to add these in, so try
10473             # to allow for either
10474 69 50       126 if ($pos + $len > $size) {
10475 0         0 $len -= 4;
10476 0 0 0     0 last if $pos + $len > $size or $len < 0;
10477             }
10478             }
10479             # ignore any empty entries (or null padding) after the first
10480 71 50 33     153 next if not $len and $pos;
10481 71         113 my $str = substr($val, $pos, $len);
10482 71         85 my ($langInfo, $enc);
10483 71 50 33     227 if (($lang < 0x400 or $lang == 0x7fff) and $str !~ /^\xfe\xff/) {
      33        
10484             # this is a Macintosh language code
10485             # a language code of 0 is Macintosh english, so treat as default
10486 71 50       100 if ($lang) {
10487 0 0       0 if ($lang == 0x7fff) {
10488             # technically, ISO 639-2 doesn't have a 2-character
10489             # equivalent for 'und', but use 'un' anyway
10490 0         0 $lang = 'un';
10491             } else {
10492             # use Font.pm to look up language string
10493 0         0 require Image::ExifTool::Font;
10494 0         0 $lang = $Image::ExifTool::Font::ttLang{Macintosh}{$lang};
10495             }
10496             } else {
10497             # for the default language code of 0x0000, use UTF-8 instead
10498             # of the CharsetQuickTime setting if obviously UTF8
10499 71 50       181 $enc = 'UTF8' if Image::ExifTool::IsUTF8(\$str) > 0;
10500             }
10501             # the spec says only "Macintosh text encoding", but
10502             # allow this to be configured by the user
10503 71 50       127 $enc = $charsetQuickTime unless $enc;
10504             } else {
10505             # convert language code to ASCII (ignore read-only bit)
10506 0         0 $lang = UnpackLang($lang);
10507             # may be either UTF-8 or UTF-16BE
10508 0 0       0 $enc = $str=~s/^\xfe\xff// ? 'UTF16' : 'UTF8';
10509             }
10510 71 100       147 unless ($$tagInfo{NoDecode}) {
10511 70         167 $str = $et->Decode($str, $enc);
10512 70         126 $str =~ s/\0+$//; # remove any trailing nulls (eg. 3gp tags)
10513             }
10514 71 100 100     142 if ($$tagInfo{IText} and $$tagInfo{IText} > 6) {
10515 1         3 my $n = $$tagInfo{IText} - 6;
10516             # add back extra bytes (eg. 'rtng' box)
10517 1         3 $str = substr($val, $pos-$n-2, $n) . $str;
10518             }
10519 71 50       112 $langInfo = GetLangInfoQT($et, $tagInfo, $lang) if $lang;
10520 71   33     257 push @found, $et->FoundTag($langInfo || $tagInfo, $str);
10521 71         140 $pos += $len;
10522             }
10523             } else {
10524 331         652 my $format = $$tagInfo{Format};
10525 331 100       500 if ($format) {
10526 102         414 $val = ReadValue(\$val, 0, $format, $$tagInfo{Count}, length($val));
10527             }
10528 331         440 my $oldBase;
10529 331 50       640 if ($$tagInfo{SetBase}) {
10530 0         0 $oldBase = $$et{BASE};
10531 0         0 $$et{BASE} = $dataPos;
10532             }
10533 331         870 my $key = $et->FoundTag($tagInfo, $val);
10534 331         581 push @found, $key;
10535 331 50       548 $$et{BASE} = $oldBase if defined $oldBase;
10536             # decode if necessary (NOTE: must be done after RawConv)
10537 331 50 66     1705 if (defined $key and (not $format or $format =~ /^string/) and
      100        
      100        
      66        
      100        
      66        
      66        
10538             not $$tagInfo{Unknown} and not $$tagInfo{ValueConv} and
10539             not $$tagInfo{Binary} and defined $$et{VALUE}{$key} and not ref $val)
10540             {
10541 20         45 my $vp = \$$et{VALUE}{$key};
10542 20 50 66     130 if (not ref $$vp and length($$vp) <= 65536 and $$vp =~ /[\x80-\xff]/) {
      66        
10543             # the encoding of this is not specified, so use CharsetQuickTime
10544             # unless the string is valid UTF-8
10545 0 0       0 my $enc = Image::ExifTool::IsUTF8($vp) > 0 ? 'UTF8' : $charsetQuickTime;
10546 0         0 $$vp = $et->Decode($$vp, $enc);
10547             }
10548             }
10549             }
10550             # tweak family 1 group names for Keys/ItemList/UserData tags in a track
10551 1454 100 66     5263 if ($$et{SET_GROUP1} and ($dirID eq 'ilst' or $dirID eq 'udta') and @found) {
      100        
      66        
10552 4         8 my $type = $trackPath{join '-', @{$$et{PATH}}};
  4         20  
10553 4 50       12 if ($type) {
10554 4   33     18 my $grp = ($avType{$$et{MediaType}} || $$et{SET_GROUP1}) . $type;
10555 4   33     23 defined and $et->SetGroup($_, $grp) foreach @found;
10556             }
10557             }
10558             }
10559             } else {
10560 88 50       150 $et->VerboseInfo($tag, $tagInfo,
10561             Size => $size,
10562             Extra => sprintf(' at offset 0x%.4x', $raf->Tell()),
10563             ) if $verbose;
10564 88         193 my $seekTo = $raf->Tell() + $size;
10565 88 0 33     125 if ($tagInfo and $$tagInfo{DontRead} and $$tagInfo{SubDirectory}) {
      0        
10566             # ignore first trailer if it is the payload of this box
10567 0 0 0     0 $trailer = $$trailer[3] if $trailer and $$trailer[1] == $raf->Tell();
10568 0         0 my $subdir = $$tagInfo{SubDirectory};
10569             my %dirInfo = (
10570             RAF => $raf,
10571             DirName => $$tagInfo{Name},
10572 0         0 DirID => $tag,
10573             DirEnd => $seekTo,
10574             );
10575 0         0 my $subTable = GetTagTable($$subdir{TagTable});
10576 0         0 my $proc = $$subdir{ProcessProc};
10577             # make ProcessMOV() the default processing procedure for subdirectories
10578 0 0 0     0 $proc = \&ProcessMOV unless $proc or $$subTable{PROCESS_PROC};
10579 0         0 $et->ProcessDirectory(\%dirInfo, $subTable, $proc);
10580 0         0 $raf->Seek($seekTo);
10581             }
10582 88 50 33     191 unless ($raf->Seek($seekTo-1) and $raf->Read($buff, 1) == 1) {
10583 0 0       0 if (pack('N',$size) =~ /^]/) { # check for corrupted HEIC file downloaded from heic.digital
10584 0         0 $warnStr = sprintf('Extraneous HTML text appended to file at offset 0x%x', $lastPos);
10585             } else {
10586 0         0 my $t = PrintableTagID($tag,2);
10587 0         0 $warnStr = sprintf("Truncated '${t}' data at offset 0x%x", $lastPos);
10588             }
10589 0         0 last;
10590             }
10591             }
10592 1576 100       2514 $$et{MediaType} = '' if $tag eq 'trak'; # reset track type at end of track
10593 1576         2102 $dataPos += $size + 8; # point to start of next atom data
10594 1576 100 100     3883 last if $dirEnd and $dataPos >= $dirEnd; # (note: ignores last value if 0 bytes)
10595 1115         2636 $lastPos = $raf->Tell() + $dirBase;
10596 1115 50 33     1967 if ($trailer and $lastPos >= $$trailer[1]) {
10597 0         0 $et->Warn(sprintf('%s trailer at offset 0x%x (%d bytes)', @$trailer[0..2]), 1);
10598 0         0 last;
10599             }
10600 1115 100       2318 $raf->Read($buff, 8) == 8 or last;
10601 1090 100 100     3800 $lastTag = $tag if $$tagTablePtr{$tag} and $tag ne 'free'; # (Insta360 sometimes puts free block before trailer)
10602 1090         2559 ($size, $tag) = unpack('Na4', $buff);
10603 1090 100       2249 ++$index if defined $index;
10604             }
10605 486 50       764 if ($warnStr) {
10606             # assume this is an unknown trailer if it comes immediately after
10607             # mdat or moov and has a tag name we don't recognize
10608 0 0 0     0 if (($lastTag eq 'mdat' or $lastTag eq 'moov') and
      0        
      0        
10609             (not $$tagTablePtr{$tag} or ref $$tagTablePtr{$tag} eq 'HASH' and $$tagTablePtr{$tag}{Unknown}))
10610             {
10611 0         0 $et->Warn('Unknown trailer with '.lcfirst($warnStr));
10612             } else {
10613 0         0 $et->Warn($warnStr);
10614             }
10615             }
10616             # tweak file type based on track content ("iso*" and "dash" ftyp only ["mp42" added in 13.39])
10617 486 0 66     883 if ($topLevel and $$et{FileType} and $$et{FileType} eq 'MP4' and
      33        
      33        
      0        
      0        
      0        
      0        
10618             $$et{save_ftyp} and $$et{HasHandler} and $$et{save_ftyp} =~ /^(iso|dash|mp42)/ and
10619             $$et{HasHandler}{soun} and not $$et{HasHandler}{vide})
10620             {
10621 0         0 $et->OverrideFileType('M4A', 'audio/mp4');
10622             }
10623             # fill in missing defaults for alternate language tags
10624             # (the first language is taken as the default)
10625 486 100 100     835 if ($doDefaultLang and $$et{QTLang}) {
10626 2         4 QTLang: foreach $tag (@{$$et{QTLang}}) {
  2         8  
10627 3 50       10 next unless defined $$et{VALUE}{$tag};
10628 3 50       9 my $langInfo = $$et{TAG_INFO}{$tag} or next;
10629 3 50       7 my $tagInfo = $$langInfo{SrcTagInfo} or next;
10630 3         6 my $infoHash = $$et{TAG_INFO};
10631 3         8 my $name = $$tagInfo{Name};
10632             # loop through all instances of this tag name and generate the default-language
10633             # version only if we don't already have a QuickTime tag with this name
10634 3         6 my ($i, $key);
10635 3         9 for ($i=0, $key=$name; $$infoHash{$key}; ++$i, $key="$name ($i)") {
10636 3 50       14 next QTLang if $et->GetGroup($key, 0) eq 'QuickTime';
10637             }
10638 0         0 my $oldRawConv = $$tagInfo{RawConv};
10639 0 0       0 delete $$tagInfo{RawConv} if defined $oldRawConv; # (avoid doing RawConv twice)
10640 0         0 $key = $et->FoundTag($tagInfo, $$et{VALUE}{$tag});
10641 0 0       0 $$tagInfo{RawConv} = $oldRawConv if defined $oldRawConv;
10642             # copy extra tag information (groups, etc) to the synthetic tag
10643 0         0 $$et{TAG_EXTRA}{$key} = $$et{TAG_EXTRA}{$tag};
10644 0         0 $et->VPrint(0, "(synthesized default-language tag for QuickTime:$$tagInfo{Name})");
10645             }
10646 2         5 delete $$et{QTLang};
10647             }
10648             # process item information now that we are done processing its 'meta' container
10649 486 100 100     1380 HandleItemInfo($et) if $topLevel or $dirID eq 'meta';
10650              
10651             # process linked list of trailers
10652 486         811 for (; $trailer; $trailer=$$trailer[3]) {
10653 0 0       0 next if $lastPos > $$trailer[1]; # skip if we have already processed this as an atom
10654 0 0       0 last unless $raf->Seek($$trailer[1], 0);
10655 0 0 0     0 if ($$trailer[0] eq 'LigoGPS' and $raf->Read($buff, 8) == 8 and $buff =~ /skip$/i) {
    0 0        
    0 0        
10656 0 0       0 $ee or $et->Warn('Use the ExtractEmbedded option to decode timed GPS',3), next;
10657 0         0 my $len = Get32u(\$buff, 0) - 16;
10658 0 0 0     0 if ($len > 0 and $raf->Read($buff, $len) == $len and $buff =~ /^LIGOGPSINFO\0/) {
      0        
10659 0         0 my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
10660 0         0 my %dirInfo = ( DataPt => \$buff, DataPos => $$trailer[1] + 8, DirName => 'LigoGPSTrailer' );
10661 0         0 $et->VerboseDump(\$buff, DataPos => $dirInfo{DataPos});
10662 0         0 Image::ExifTool::LigoGPS::ProcessLigoGPS($et, \%dirInfo, $tbl);
10663             } else {
10664 0         0 $et->Warn('Unrecognized data in LigoGPS trailer');
10665             }
10666             } elsif ($$trailer[0] eq 'Insta360' and $ee) {
10667             # process Insta360 trailer if it exists
10668 0 0       0 $raf->Seek(0, 2) or $et->Warn('Seek error'), last;
10669 0         0 my $offset = $raf->Tell() - $$trailer[1] - $$trailer[2];
10670 0         0 ProcessInsta360($et, { RAF => $raf, DirName => $$trailer[0], Offset => $offset });
10671             } elsif ($$trailer[0] eq 'MIE') {
10672 0         0 require Image::ExifTool::MIE;
10673 0         0 Image::ExifTool::MIE::ProcessMIE($et, { RAF => $raf, DirName => 'MIE', Trailer => 1 });
10674             }
10675             }
10676             # brute force scan for metadata embedded in media data
10677             # (and process Insta360 trailer if it exists)
10678 486 100 100     985 ScanMediaData($et) if $ee and $topLevel and not $$et{OPTIONS}{FastScan};
      66        
10679              
10680             # restore any changed options
10681 486         935 $et->Options($_ => $saveOptions{$_}) foreach keys %saveOptions;
10682 486         1956 return 1;
10683             }
10684              
10685             #------------------------------------------------------------------------------
10686             # Process a QuickTime Image File
10687             # Inputs: 0) ExifTool object reference, 1) directory information reference
10688             # Returns: 1 on success
10689             sub ProcessQTIF($$)
10690             {
10691 0     0 0   my ($et, $dirInfo) = @_;
10692 0           my $table = GetTagTable('Image::ExifTool::QuickTime::ImageFile');
10693 0           return ProcessMOV($et, $dirInfo, $table);
10694             }
10695              
10696             #==============================================================================
10697             # Autoload LigoGPS module if necessary
10698             # NOTE: Switches to package LigoGPS!
10699             #
10700             package Image::ExifTool::LigoGPS;
10701 31     31   352 use vars qw($AUTOLOAD);
  31         80  
  31         3788  
10702             sub AUTOLOAD {
10703 0     0     require Image::ExifTool::LigoGPS;
10704 0 0         unless (defined &$AUTOLOAD) {
10705 0           my @caller = caller(0);
10706             # reproduce Perl's standard 'undefined subroutine' message:
10707 0           die "Undefined subroutine $AUTOLOAD called at $caller[1] line $caller[2]\n";
10708             }
10709 31     31   225 no strict 'refs';
  31         56  
  31         37961  
10710 0           return &$AUTOLOAD(@_); # call the function
10711             }
10712             #==============================================================================
10713              
10714             1; # end
10715              
10716             __END__