source: gs2-extensions/parallel-building/trunk/src/perllib/cpan/Image/ExifTool/QuickTime.pm@ 24626

Last change on this file since 24626 was 24626, checked in by jmt12, 13 years ago

An (almost) complete copy of the perllib directory from a (circa SEP2011) head checkout from Greenstone 2 trunk - in order to try and make merging in this extension a little easier later on (as there have been some major changes to buildcol.pl commited in the main trunk but not in the x64 branch)

File size: 94.0 KB
Line 
1#------------------------------------------------------------------------------
2# File: QuickTime.pm
3#
4# Description: Read QuickTime, MP4 and M4A 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: 1) http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html
12# 2) http://search.cpan.org/dist/MP4-Info-1.04/
13# 3) http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
14# 4) http://wiki.multimedia.cx/index.php?title=Apple_QuickTime
15# 5) ISO 14496-12 (http://neuron2.net/library/avc/c041828_ISO_IEC_14496-12_2005(E).pdf)
16# 6) ISO 14496-16 (http://www.iec-normen.de/previewpdf/info_isoiec14496-16%7Bed2.0%7Den.pdf)
17# 7) http://atomicparsley.sourceforge.net/mpeg-4files.html
18# 8) http://wiki.multimedia.cx/index.php?title=QuickTime_container
19# 9) http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart3.pdf (Oct 2008)
20# 10) http://code.google.com/p/mp4v2/wiki/iTunesMetadata
21# 11) http://www.canieti.com.mx/assets/files/1011/IEC_100_1384_DC.pdf
22# 12) QuickTime file format specification 2010-05-03
23# 13) http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf
24# 14) http://standards.iso.org/ittf/PubliclyAvailableStandards/c051533_ISO_IEC_14496-12_2008.zip
25#------------------------------------------------------------------------------
26
27package Image::ExifTool::QuickTime;
28
29use strict;
30use vars qw($VERSION);
31use Image::ExifTool qw(:DataAccess :Utils);
32use Image::ExifTool::Exif;
33
34$VERSION = '1.48';
35
36sub FixWrongFormat($);
37sub ProcessMOV($$;$);
38sub ProcessKeys($$$);
39sub ProcessMetaData($$$);
40sub ConvertISO6709($);
41sub PrintGPSCoordinates($);
42
43# information for time/date-based tags (time zero is Jan 1, 1904)
44my %timeInfo = (
45 # Note: This value will be in UTC if generated by a system that is aware of the time zone
46 ValueConv => 'ConvertUnixTime($val - ((66 * 365 + 17) * 24 * 3600))',
47 PrintConv => '$self->ConvertDateTime($val)',
48 # (can't put Groups here because they aren't constant!)
49);
50# information for duration tags
51my %durationInfo = (
52 ValueConv => '$$self{TimeScale} ? $val / $$self{TimeScale} : $val',
53 PrintConv => '$$self{TimeScale} ? ConvertDuration($val) : $val',
54);
55
56# 4-character Vendor ID codes (ref PH)
57my %vendorID = (
58 appl => 'Apple',
59 fe20 => 'Olympus (fe20)', # (FE200)
60 FFMP => 'FFmpeg',
61 'GIC '=> 'General Imaging Co.',
62 kdak => 'Kodak',
63 KMPI => 'Konica-Minolta',
64 mino => 'Minolta',
65 niko => 'Nikon',
66 NIKO => 'Nikon',
67 olym => 'Olympus',
68 pana => 'Panasonic',
69 pent => 'Pentax',
70 pr01 => 'Olympus (pr01)', # (FE100,FE110,FE115)
71 sany => 'Sanyo',
72 'SMI '=> 'Sorenson Media Inc.',
73 ZORA => 'Zoran Corporation',
74);
75
76# QuickTime data atom encodings for string types (ref 12)
77my %stringEncoding = (
78 1 => 'UTF8',
79 2 => 'UTF16',
80 3 => 'ShiftJIS',
81 4 => 'UTF8',
82 5 => 'UTF16',
83);
84
85# MIME types for all entries in the ftypLookup with file extensions
86# (defaults to 'video/mp4' if not found in this lookup)
87my %mimeLookup = (
88 '3G2' => 'video/3gpp2',
89 '3GP' => 'video/3gpp',
90 DVB => 'video/vnd.dvb.file',
91 F4A => 'audio/mp4',
92 F4B => 'audio/mp4',
93 JP2 => 'image/jp2',
94 JPM => 'image/jpm',
95 JPX => 'image/jpx',
96 M4A => 'audio/mp4',
97 M4B => 'audio/mp4',
98 M4P => 'audio/mp4',
99 M4V => 'video/x-m4v',
100 MOV => 'video/quicktime',
101 MQV => 'video/quicktime',
102);
103
104my %graphicsMode = (
105 # (ref http://homepage.mac.com/vanhoek/MovieGuts%20docs/64.html)
106 0x00 => 'srcCopy',
107 0x01 => 'srcOr',
108 0x02 => 'srcXor',
109 0x03 => 'srcBic',
110 0x04 => 'notSrcCopy',
111 0x05 => 'notSrcOr',
112 0x06 => 'notSrcXor',
113 0x07 => 'notSrcBic',
114 0x08 => 'patCopy',
115 0x09 => 'patOr',
116 0x0a => 'patXor',
117 0x0b => 'patBic',
118 0x0c => 'notPatCopy',
119 0x0d => 'notPatOr',
120 0x0e => 'notPatXor',
121 0x0f => 'notPatBic',
122 0x20 => 'blend',
123 0x21 => 'addPin',
124 0x22 => 'addOver',
125 0x23 => 'subPin',
126 0x24 => 'transparent',
127 0x25 => 'addMax',
128 0x26 => 'subOver',
129 0x27 => 'addMin',
130 0x31 => 'grayishTextOr',
131 0x32 => 'hilite',
132 0x40 => 'ditherCopy',
133 # the following ref ISO/IEC 15444-3
134 0x100 => 'Alpha',
135 0x101 => 'White Alpha',
136 0x102 => 'Pre-multiplied Black Alpha',
137 0x110 => 'Component Alpha',
138);
139
140# look up file type from ftyp atom type, with MIME type in comment if known
141# (ref http://www.ftyps.com/)
142my %ftypLookup = (
143 '3g2a' => '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-0 V1.0', # video/3gpp2
144 '3g2b' => '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-A V1.0.0', # video/3gpp2
145 '3g2c' => '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-B v1.0', # video/3gpp2
146 '3ge6' => '3GPP (.3GP) Release 6 MBMS Extended Presentations', # video/3gpp
147 '3ge7' => '3GPP (.3GP) Release 7 MBMS Extended Presentations', # video/3gpp
148 '3gg6' => '3GPP Release 6 General Profile', # video/3gpp
149 '3gp1' => '3GPP Media (.3GP) Release 1 (probably non-existent)', # video/3gpp
150 '3gp2' => '3GPP Media (.3GP) Release 2 (probably non-existent)', # video/3gpp
151 '3gp3' => '3GPP Media (.3GP) Release 3 (probably non-existent)', # video/3gpp
152 '3gp4' => '3GPP Media (.3GP) Release 4', # video/3gpp
153 '3gp5' => '3GPP Media (.3GP) Release 5', # video/3gpp
154 '3gp6' => '3GPP Media (.3GP) Release 6 Basic Profile', # video/3gpp
155 '3gp6' => '3GPP Media (.3GP) Release 6 Progressive Download', # video/3gpp
156 '3gp6' => '3GPP Media (.3GP) Release 6 Streaming Servers', # video/3gpp
157 '3gs7' => '3GPP Media (.3GP) Release 7 Streaming Servers', # video/3gpp
158 'avc1' => 'MP4 Base w/ AVC ext [ISO 14496-12:2005]', # video/mp4
159 'CAEP' => 'Canon Digital Camera',
160 'caqv' => 'Casio Digital Camera',
161 'CDes' => 'Convergent Design',
162 'da0a' => 'DMB MAF w/ MPEG Layer II aud, MOT slides, DLS, JPG/PNG/MNG images',
163 'da0b' => 'DMB MAF, extending DA0A, with 3GPP timed text, DID, TVA, REL, IPMP',
164 'da1a' => 'DMB MAF audio with ER-BSAC audio, JPG/PNG/MNG images',
165 'da1b' => 'DMB MAF, extending da1a, with 3GPP timed text, DID, TVA, REL, IPMP',
166 'da2a' => 'DMB MAF aud w/ HE-AAC v2 aud, MOT slides, DLS, JPG/PNG/MNG images',
167 'da2b' => 'DMB MAF, extending da2a, with 3GPP timed text, DID, TVA, REL, IPMP',
168 'da3a' => 'DMB MAF aud with HE-AAC aud, JPG/PNG/MNG images',
169 'da3b' => 'DMB MAF, extending da3a w/ BIFS, 3GPP timed text, DID, TVA, REL, IPMP',
170 'dmb1' => 'DMB MAF supporting all the components defined in the specification',
171 'dmpf' => 'Digital Media Project', # various
172 'drc1' => 'Dirac (wavelet compression), encapsulated in ISO base media (MP4)',
173 'dv1a' => 'DMB MAF vid w/ AVC vid, ER-BSAC aud, BIFS, JPG/PNG/MNG images, TS',
174 'dv1b' => 'DMB MAF, extending dv1a, with 3GPP timed text, DID, TVA, REL, IPMP',
175 'dv2a' => 'DMB MAF vid w/ AVC vid, HE-AAC v2 aud, BIFS, JPG/PNG/MNG images, TS',
176 'dv2b' => 'DMB MAF, extending dv2a, with 3GPP timed text, DID, TVA, REL, IPMP',
177 'dv3a' => 'DMB MAF vid w/ AVC vid, HE-AAC aud, BIFS, JPG/PNG/MNG images, TS',
178 'dv3b' => 'DMB MAF, extending dv3a, with 3GPP timed text, DID, TVA, REL, IPMP',
179 'dvr1' => 'DVB (.DVB) over RTP', # video/vnd.dvb.file
180 'dvt1' => 'DVB (.DVB) over MPEG-2 Transport Stream', # video/vnd.dvb.file
181 'F4A ' => 'Audio for Adobe Flash Player 9+ (.F4A)', # audio/mp4
182 'F4B ' => 'Audio Book for Adobe Flash Player 9+ (.F4B)', # audio/mp4
183 'F4P ' => 'Protected Video for Adobe Flash Player 9+ (.F4P)', # video/mp4
184 'F4V ' => 'Video for Adobe Flash Player 9+ (.F4V)', # video/mp4
185 'isc2' => 'ISMACryp 2.0 Encrypted File', # ?/enc-isoff-generic
186 'iso2' => 'MP4 Base Media v2 [ISO 14496-12:2005]', # video/mp4
187 'isom' => 'MP4 Base Media v1 [IS0 14496-12:2003]', # video/mp4
188 'JP2 ' => 'JPEG 2000 Image (.JP2) [ISO 15444-1 ?]', # image/jp2
189 'JP20' => 'Unknown, from GPAC samples (prob non-existent)',
190 'jpm ' => 'JPEG 2000 Compound Image (.JPM) [ISO 15444-6]', # image/jpm
191 'jpx ' => 'JPEG 2000 with extensions (.JPX) [ISO 15444-2]', # image/jpx
192 'KDDI' => '3GPP2 EZmovie for KDDI 3G cellphones', # video/3gpp2
193 'M4A ' => 'Apple iTunes AAC-LC (.M4A) Audio', # audio/x-m4a
194 'M4B ' => 'Apple iTunes AAC-LC (.M4B) Audio Book', # audio/mp4
195 'M4P ' => 'Apple iTunes AAC-LC (.M4P) AES Protected Audio', # audio/mp4
196 'M4V ' => 'Apple iTunes Video (.M4V) Video', # video/x-m4v
197 'M4VH' => 'Apple TV (.M4V)', # video/x-m4v
198 'M4VP' => 'Apple iPhone (.M4V)', # video/x-m4v
199 'mj2s' => 'Motion JPEG 2000 [ISO 15444-3] Simple Profile', # video/mj2
200 'mjp2' => 'Motion JPEG 2000 [ISO 15444-3] General Profile', # video/mj2
201 'mmp4' => 'MPEG-4/3GPP Mobile Profile (.MP4/3GP) (for NTT)', # video/mp4
202 'mp21' => 'MPEG-21 [ISO/IEC 21000-9]', # various
203 'mp41' => 'MP4 v1 [ISO 14496-1:ch13]', # video/mp4
204 'mp42' => 'MP4 v2 [ISO 14496-14]', # video/mp4
205 'mp71' => 'MP4 w/ MPEG-7 Metadata [per ISO 14496-12]', # various
206 'MPPI' => 'Photo Player, MAF [ISO/IEC 23000-3]', # various
207 'mqt ' => 'Sony / Mobile QuickTime (.MQV) US Patent 7,477,830 (Sony Corp)', # video/quicktime
208 'MSNV' => 'MPEG-4 (.MP4) for SonyPSP', # audio/mp4
209 'NDAS' => 'MP4 v2 [ISO 14496-14] Nero Digital AAC Audio', # audio/mp4
210 'NDSC' => 'MPEG-4 (.MP4) Nero Cinema Profile', # video/mp4
211 'NDSH' => 'MPEG-4 (.MP4) Nero HDTV Profile', # video/mp4
212 'NDSM' => 'MPEG-4 (.MP4) Nero Mobile Profile', # video/mp4
213 'NDSP' => 'MPEG-4 (.MP4) Nero Portable Profile', # video/mp4
214 'NDSS' => 'MPEG-4 (.MP4) Nero Standard Profile', # video/mp4
215 'NDXC' => 'H.264/MPEG-4 AVC (.MP4) Nero Cinema Profile', # video/mp4
216 'NDXH' => 'H.264/MPEG-4 AVC (.MP4) Nero HDTV Profile', # video/mp4
217 'NDXM' => 'H.264/MPEG-4 AVC (.MP4) Nero Mobile Profile', # video/mp4
218 'NDXP' => 'H.264/MPEG-4 AVC (.MP4) Nero Portable Profile', # video/mp4
219 'NDXS' => 'H.264/MPEG-4 AVC (.MP4) Nero Standard Profile', # video/mp4
220 'odcf' => 'OMA DCF DRM Format 2.0 (OMA-TS-DRM-DCF-V2_0-20060303-A)', # various
221 'opf2' => 'OMA PDCF DRM Format 2.1 (OMA-TS-DRM-DCF-V2_1-20070724-C)',
222 'opx2' => 'OMA PDCF DRM + XBS extensions (OMA-TS-DRM_XBS-V1_0-20070529-C)',
223 'pana' => 'Panasonic Digital Camera',
224 'qt ' => 'Apple QuickTime (.MOV/QT)', # video/quicktime
225 'ROSS' => 'Ross Video',
226 'sdv ' => 'SD Memory Card Video', # various?
227 'ssc1' => 'Samsung stereoscopic, single stream',
228 'ssc2' => 'Samsung stereoscopic, dual stream',
229);
230
231# QuickTime atoms
232%Image::ExifTool::QuickTime::Main = (
233 PROCESS_PROC => \&ProcessMOV,
234 GROUPS => { 2 => 'Video' },
235 NOTES => q{
236 The QuickTime format is used for many different types of audio, video and
237 image files. Exiftool extracts meta information from the UserData atom
238 (including some proprietary manufacturer-specific information), as well as
239 extracting various audio, video and image parameters. Tags with a question
240 mark after their name are not extracted unless the Unknown option is set.
241 },
242 free => { Unknown => 1, Binary => 1 },
243 skip => { Unknown => 1, Binary => 1 },
244 wide => { Unknown => 1, Binary => 1 },
245 ftyp => { #MP4
246 Name => 'FileType',
247 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::FileType' },
248 },
249 pnot => {
250 Name => 'Preview',
251 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Preview' },
252 },
253 PICT => {
254 Name => 'PreviewPICT',
255 Binary => 1,
256 },
257 pict => { #8
258 Name => 'PreviewPICT',
259 Binary => 1,
260 },
261 moov => {
262 Name => 'Movie',
263 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Movie' },
264 },
265 mdat => { Name => 'MovieData', Unknown => 1, Binary => 1 },
266 'mdat-size' => {
267 Name => 'MovieDataSize',
268 Notes => q{
269 not a real tag ID, this tag represents the size of the 'mdat' data in bytes
270 and is used in the AvgBitrate calculation
271 },
272 },
273 junk => { Unknown => 1, Binary => 1 }, #8
274 uuid => [
275 { #9 (MP4 files)
276 Name => 'UUID-XMP',
277 Condition => '$$valPt=~/^\xbe\x7a\xcf\xcb\x97\xa9\x42\xe8\x9c\x71\x99\x94\x91\xe3\xaf\xac/',
278 SubDirectory => {
279 TagTable => 'Image::ExifTool::XMP::Main',
280 Start => 16,
281 },
282 },
283 { #11 (MP4 files)
284 Name => 'UUID-PROF',
285 Condition => '$$valPt=~/^PROF!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
286 SubDirectory => {
287 TagTable => 'Image::ExifTool::QuickTime::Profile',
288 Start => 24, # uid(16) + version(1) + flags(3) + count(4)
289 },
290 },
291 { #PH (Flip MP4 files)
292 Name => 'UUID-Flip',
293 Condition => '$$valPt=~/\x4a\xb0\x3b\x0f\x61\x8d\x40\x75\x82\xb2\xd9\xfa\xce\xd3\x5f\xf5/',
294 SubDirectory => {
295 TagTable => 'Image::ExifTool::QuickTime::Flip',
296 Start => 16,
297 },
298 },
299 # "\x98\x7f\xa3\xdf\x2a\x85\x43\xc0\x8f\x8f\xd9\x7c\x47\x1e\x8e\xea" - unknown data in Flip videos
300 { #8
301 Name => 'UUID-Unknown',
302 Unknown => 1,
303 Binary => 1,
304 },
305 ],
306);
307
308# MPEG-4 'ftyp' atom
309# (ref http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html)
310%Image::ExifTool::QuickTime::FileType = (
311 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
312 GROUPS => { 2 => 'Video' },
313 FORMAT => 'int32u',
314 0 => {
315 Name => 'MajorBrand',
316 Format => 'undef[4]',
317 PrintConv => \%ftypLookup,
318 },
319 1 => {
320 Name => 'MinorVersion',
321 Format => 'undef[4]',
322 ValueConv => 'sprintf("%x.%x.%x", unpack("nCC", $val))',
323 },
324 2 => {
325 Name => 'CompatibleBrands',
326 Format => 'undef[$size-8]',
327 # ignore any entry with a null, and return others as a list
328 ValueConv => 'my @a=($val=~/.{4}/sg); @a=grep(!/\0/,@a); \@a',
329 },
330);
331
332# atoms used in QTIF files
333%Image::ExifTool::QuickTime::ImageFile = (
334 PROCESS_PROC => \&ProcessMOV,
335 GROUPS => { 2 => 'Image' },
336 NOTES => 'Tags used in QTIF QuickTime Image Files.',
337 idsc => {
338 Name => 'ImageDescription',
339 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ImageDesc' },
340 },
341 idat => {
342 Name => 'ImageData',
343 Binary => 1,
344 },
345 iicc => {
346 Name => 'ICC_Profile',
347 SubDirectory => { TagTable => 'Image::ExifTool::ICC_Profile::Main' },
348 },
349);
350
351# image description data block
352%Image::ExifTool::QuickTime::ImageDesc = (
353 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
354 GROUPS => { 2 => 'Image' },
355 FORMAT => 'int16u',
356 2 => {
357 Name => 'CompressorID',
358 Format => 'string[4]',
359# not very useful since this isn't a complete list and name is given below
360# # ref http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html
361# PrintConv => {
362# cvid => 'Cinepak',
363# jpeg => 'JPEG',
364# 'smc '=> 'Graphics',
365# 'rle '=> 'Animation',
366# rpza => 'Apple Video',
367# kpcd => 'Kodak Photo CD',
368# 'png '=> 'Portable Network Graphics',
369# mjpa => 'Motion-JPEG (format A)',
370# mjpb => 'Motion-JPEG (format B)',
371# SVQ1 => 'Sorenson video, version 1',
372# SVQ3 => 'Sorenson video, version 3',
373# mp4v => 'MPEG-4 video',
374# 'dvc '=> 'NTSC DV-25 video',
375# dvcp => 'PAL DV-25 video',
376# 'gif '=> 'Compuserve Graphics Interchange Format',
377# h263 => 'H.263 video',
378# tiff => 'Tagged Image File Format',
379# 'raw '=> 'Uncompressed RGB',
380# '2vuY'=> "Uncompressed Y'CbCr, 3x8-bit 4:2:2 (2vuY)",
381# 'yuv2'=> "Uncompressed Y'CbCr, 3x8-bit 4:2:2 (yuv2)",
382# v308 => "Uncompressed Y'CbCr, 8-bit 4:4:4",
383# v408 => "Uncompressed Y'CbCr, 8-bit 4:4:4:4",
384# v216 => "Uncompressed Y'CbCr, 10, 12, 14, or 16-bit 4:2:2",
385# v410 => "Uncompressed Y'CbCr, 10-bit 4:4:4",
386# v210 => "Uncompressed Y'CbCr, 10-bit 4:2:2",
387# },
388 },
389 10 => {
390 Name => 'VendorID',
391 Format => 'string[4]',
392 RawConv => 'length $val ? $val : undef',
393 PrintConv => \%vendorID,
394 SeparateTable => 'VendorID',
395 },
396 # 14 - ("Quality" in QuickTime docs) ??
397 16 => 'SourceImageWidth',
398 17 => 'SourceImageHeight',
399 18 => { Name => 'XResolution', Format => 'fixed32u' },
400 20 => { Name => 'YResolution', Format => 'fixed32u' },
401 # 24 => 'FrameCount', # always 1 (what good is this?)
402 25 => {
403 Name => 'CompressorName',
404 Format => 'string[32]',
405 # (sometimes this is a Pascal string, and sometimes it is a C string)
406 RawConv => q{
407 $val=substr($val,1,ord($1)) if $val=~/^([\0-\x1f])/ and ord($1)<length($val);
408 length $val ? $val : undef;
409 },
410 },
411 41 => 'BitDepth',
412);
413
414# preview data block
415%Image::ExifTool::QuickTime::Preview = (
416 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
417 GROUPS => { 2 => 'Image' },
418 FORMAT => 'int16u',
419 0 => {
420 Name => 'PreviewDate',
421 Format => 'int32u',
422 Groups => { 2 => 'Time' },
423 %timeInfo,
424 },
425 2 => 'PreviewVersion',
426 3 => {
427 Name => 'PreviewAtomType',
428 Format => 'string[4]',
429 },
430 5 => 'PreviewAtomIndex',
431);
432
433# movie atoms
434%Image::ExifTool::QuickTime::Movie = (
435 PROCESS_PROC => \&ProcessMOV,
436 GROUPS => { 2 => 'Video' },
437 mvhd => {
438 Name => 'MovieHeader',
439 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MovieHeader' },
440 },
441 trak => {
442 Name => 'Track',
443 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Track' },
444 },
445 udta => {
446 Name => 'UserData',
447 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::UserData' },
448 },
449 meta => { # 'meta' is found here in my EX-F1 MOV sample - PH
450 Name => 'Meta',
451 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
452 },
453 iods => {
454 Name => 'InitialObjectDescriptor',
455 Flags => ['Binary','Unknown'],
456 },
457 uuid => [
458 { #11 (MP4 files) (also found in QuickTime::Track)
459 Name => 'UUID-USMT',
460 Condition => '$$valPt=~/^USMT!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
461 SubDirectory => {
462 TagTable => 'Image::ExifTool::QuickTime::UserMedia',
463 Start => 16,
464 },
465 },
466 {
467 Name => 'UUID-Unknown',
468 Unknown => 1,
469 Binary => 1,
470 },
471 ],
472 # prfl - Profile (ref 12)
473 # clip - clipping --> contains crgn (clip region) (ref 12)
474 # mvex - movie extends --> contains mehd (movie extends header), trex (track extends) (ref 14)
475);
476
477# movie header data block
478%Image::ExifTool::QuickTime::MovieHeader = (
479 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
480 GROUPS => { 2 => 'Video' },
481 FORMAT => 'int32u',
482 0 => {
483 Name => 'MovieHeaderVersion',
484 Format => 'int8u',
485 RawConv => '$$self{MovieHeaderVersion} = $val',
486 },
487 1 => {
488 Name => 'CreateDate',
489 Groups => { 2 => 'Time' },
490 %timeInfo,
491 # this is int64u if MovieHeaderVersion == 1 (ref 13)
492 Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
493 },
494 2 => {
495 Name => 'ModifyDate',
496 Groups => { 2 => 'Time' },
497 %timeInfo,
498 # this is int64u if MovieHeaderVersion == 1 (ref 13)
499 Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
500 },
501 3 => {
502 Name => 'TimeScale',
503 RawConv => '$$self{TimeScale} = $val',
504 },
505 4 => {
506 Name => 'Duration',
507 %durationInfo,
508 # this is int64u if MovieHeaderVersion == 1 (ref 13)
509 Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
510 },
511 5 => {
512 Name => 'PreferredRate',
513 ValueConv => '$val / 0x10000',
514 },
515 6 => {
516 Name => 'PreferredVolume',
517 Format => 'int16u',
518 ValueConv => '$val / 256',
519 PrintConv => 'sprintf("%.2f%%", $val * 100)',
520 },
521 9 => {
522 Name => 'MatrixStructure',
523 Format => 'fixed32s[9]',
524 # (the right column is fixed 2.30 instead of 16.16)
525 ValueConv => q{
526 my @a = split ' ',$val;
527 $_ /= 0x4000 foreach @a[2,5,8];
528 return "@a";
529 },
530 },
531 18 => { Name => 'PreviewTime', %durationInfo },
532 19 => { Name => 'PreviewDuration', %durationInfo },
533 20 => { Name => 'PosterTime', %durationInfo },
534 21 => { Name => 'SelectionTime', %durationInfo },
535 22 => { Name => 'SelectionDuration',%durationInfo },
536 23 => { Name => 'CurrentTime', %durationInfo },
537 24 => 'NextTrackID',
538);
539
540# track atoms
541%Image::ExifTool::QuickTime::Track = (
542 PROCESS_PROC => \&ProcessMOV,
543 GROUPS => { 2 => 'Video' },
544 tkhd => {
545 Name => 'TrackHeader',
546 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackHeader' },
547 },
548 udta => {
549 Name => 'UserData',
550 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::UserData' },
551 },
552 mdia => { #MP4
553 Name => 'Media',
554 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Media' },
555 },
556 meta => { #PH (MOV)
557 Name => 'Meta',
558 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
559 },
560 tref => {
561 Name => 'TrackRef',
562 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackRef' },
563 },
564 uuid => [
565 { #11 (MP4 files) (also found in QuickTime::Movie)
566 Name => 'UUID-USMT',
567 Condition => '$$valPt=~/^USMT!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
568 SubDirectory => {
569 TagTable => 'Image::ExifTool::QuickTime::UserMedia',
570 Start => 16,
571 },
572 },
573 {
574 Name => 'UUID-Unknown',
575 Unknown => 1,
576 Binary => 1,
577 },
578 ],
579 # edts - edits --> contains elst (edit list)
580 # tapt - TrackApertureModeDimensionsAID --> contains clef (TrackCleanApertureDimensionsAID),
581 # prof (TrackProductionApertureDimensionsAID), enof (TrackEncodedPixelsDimensionsAID)
582 # clip - clipping --> contains crgn (clip region)
583 # matt - track matt --> contains kmat (compressed matt)
584 # load - track loading settings
585 # imap - track input map --> contains ' in' --> contains ' ty', obid
586 # prfl - Profile (ref 12)
587);
588
589# track header data block
590%Image::ExifTool::QuickTime::TrackHeader = (
591 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
592 GROUPS => { 1 => 'Track#', 2 => 'Video' },
593 FORMAT => 'int32u',
594 0 => {
595 Name => 'TrackHeaderVersion',
596 Format => 'int8u',
597 Priority => 0,
598 RawConv => '$$self{TrackHeaderVersion} = $val',
599 },
600 1 => {
601 Name => 'TrackCreateDate',
602 Priority => 0,
603 Groups => { 2 => 'Time' },
604 %timeInfo,
605 # this is int64u if TrackHeaderVersion == 1 (ref 13)
606 Hook => '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
607 },
608 2 => {
609 Name => 'TrackModifyDate',
610 Priority => 0,
611 Groups => { 2 => 'Time' },
612 %timeInfo,
613 # this is int64u if TrackHeaderVersion == 1 (ref 13)
614 Hook => '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
615 },
616 3 => {
617 Name => 'TrackID',
618 Priority => 0,
619 },
620 5 => {
621 Name => 'TrackDuration',
622 Priority => 0,
623 %durationInfo,
624 # this is int64u if TrackHeaderVersion == 1 (ref 13)
625 Hook => '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
626 },
627 8 => {
628 Name => 'TrackLayer',
629 Format => 'int16u',
630 Priority => 0,
631 },
632 9 => {
633 Name => 'TrackVolume',
634 Format => 'int16u',
635 Priority => 0,
636 ValueConv => '$val / 256',
637 PrintConv => 'sprintf("%.2f%%", $val * 100)',
638 },
639 10 => {
640 Name => 'MatrixStructure',
641 Format => 'fixed32s[9]',
642 # (the right column is fixed 2.30 instead of 16.16)
643 ValueConv => q{
644 my @a = split ' ',$val;
645 $_ /= 0x4000 foreach @a[2,5,8];
646 return "@a";
647 },
648 },
649 19 => {
650 Name => 'ImageWidth',
651 Priority => 0,
652 RawConv => \&FixWrongFormat,
653 },
654 20 => {
655 Name => 'ImageHeight',
656 Priority => 0,
657 RawConv => \&FixWrongFormat,
658 },
659);
660
661# user data atoms
662%Image::ExifTool::QuickTime::UserData = (
663 PROCESS_PROC => \&ProcessMOV,
664 GROUPS => { 2 => 'Video' },
665 NOTES => q{
666 Tag ID's beginning with the copyright symbol (hex 0xa9) are multi-language
667 text. Alternate language tags are accessed by adding a dash followed by the
668 language/country code to the tag name. ExifTool will extract any
669 multi-language user data tags found, even if they don't exist in this table.
670 },
671 "\xa9cpy" => { Name => 'Copyright', Groups => { 2 => 'Author' } },
672 "\xa9day" => {
673 Name => 'CreateDate',
674 Groups => { 2 => 'Time' },
675 # handle values in the form "2010-02-12T13:27:14-0800" (written by Apple iPhone)
676 ValueConv => q{
677 require Image::ExifTool::XMP;
678 $val = Image::ExifTool::XMP::ConvertXMPDate($val);
679 $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
680 return $val;
681 },
682 PrintConv => '$self->ConvertDateTime($val)',
683 },
684 "\xa9ART" => 'Artist', #PH (iTunes 8.0.2)
685 "\xa9alb" => 'Album', #PH (iTunes 8.0.2)
686 "\xa9arg" => 'Arranger', #12
687 "\xa9ark" => 'ArrangerKeywords', #12
688 "\xa9cmt" => 'Comment', #PH (iTunes 8.0.2)
689 "\xa9cok" => 'ComposerKeywords', #12
690 "\xa9com" => 'Composer', #12
691 "\xa9dir" => 'Director', #12
692 "\xa9ed1" => 'Edit1',
693 "\xa9ed2" => 'Edit2',
694 "\xa9ed3" => 'Edit3',
695 "\xa9ed4" => 'Edit4',
696 "\xa9ed5" => 'Edit5',
697 "\xa9ed6" => 'Edit6',
698 "\xa9ed7" => 'Edit7',
699 "\xa9ed8" => 'Edit8',
700 "\xa9ed9" => 'Edit9',
701 "\xa9fmt" => 'Format',
702 "\xa9gen" => 'Genre', #PH (iTunes 8.0.2)
703 "\xa9grp" => 'Grouping', #PH (NC)
704 "\xa9inf" => 'Information',
705 "\xa9isr" => 'ISRCCode', #12
706 "\xa9lab" => 'RecordLabelName', #12
707 "\xa9lal" => 'RecordLabelURL', #12
708 "\xa9lyr" => 'Lyrics', #PH (NC)
709 "\xa9mak" => 'Make', #12
710 "\xa9mal" => 'MakerURL', #12
711 "\xa9mod" => 'Model', #PH
712 "\xa9nam" => 'Title', #12
713 "\xa9pdk" => 'ProducerKeywords', #12
714 "\xa9phg" => 'RecordingCopyright', #12
715 "\xa9prd" => 'Producer',
716 "\xa9prf" => 'Performers',
717 "\xa9prk" => 'PerformerKeywords', #12
718 "\xa9prl" => 'PerformerURL',
719 "\xa9dir" => 'Director', #12
720 "\xa9req" => 'Requirements',
721 "\xa9snk" => 'SubtitleKeywords', #12
722 "\xa9snm" => 'Subtitle', #12
723 "\xa9src" => 'SourceCredits', #12
724 "\xa9swf" => 'SongWriter', #12
725 "\xa9swk" => 'SongWriterKeywords', #12
726 "\xa9swr" => 'SoftwareVersion', #12
727 "\xa9too" => 'Encoder', #PH (NC)
728 "\xa9trk" => 'Track', #PH (NC)
729 "\xa9wrt" => 'Composer',
730 "\xa9xyz" => { #PH (iPhone 3GS)
731 Name => 'GPSCoordinates',
732 Groups => { 2 => 'Location' },
733 ValueConv => \&ConvertISO6709,
734 PrintConv => \&PrintGPSCoordinates,
735 },
736 name => 'Name',
737 WLOC => {
738 Name => 'WindowLocation',
739 Format => 'int16u',
740 },
741 LOOP => {
742 Name => 'LoopStyle',
743 Format => 'int32u',
744 PrintConv => {
745 1 => 'Normal',
746 2 => 'Palindromic',
747 },
748 },
749 SelO => {
750 Name => 'PlaySelection',
751 Format => 'int8u',
752 },
753 AllF => {
754 Name => 'PlayAllFrames',
755 Format => 'int8u',
756 },
757 meta => {
758 Name => 'Meta',
759 SubDirectory => {
760 TagTable => 'Image::ExifTool::QuickTime::Meta',
761 Start => 4, # must skip 4-byte version number header
762 },
763 },
764 DcMD => {
765 Name => 'DcMD',
766 SubDirectory => {
767 TagTable => 'Image::ExifTool::QuickTime::DcMD',
768 },
769 },
770 'ptv '=> {
771 Name => 'PrintToVideo',
772 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Video' },
773 },
774 'hnti'=> {
775 Name => 'HintInfo',
776 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HintInfo' },
777 },
778 'hinf' => {
779 Name => 'HintTrackInfo',
780 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HintTrackInfo' },
781 },
782 TAGS => [ #PH
783 # these tags were initially discovered in a Pentax movie,
784 # but similar information is found in videos from other manufacturers
785 {
786 Name => 'KodakTags',
787 Condition => '$$valPt =~ /^EASTMAN KODAK COMPANY/',
788 SubDirectory => {
789 TagTable => 'Image::ExifTool::Kodak::MOV',
790 ByteOrder => 'LittleEndian',
791 },
792 },
793 {
794 Name => 'KonicaMinoltaTags',
795 Condition => '$$valPt =~ /^KONICA MINOLTA DIGITAL CAMERA/',
796 SubDirectory => {
797 TagTable => 'Image::ExifTool::Minolta::MOV1',
798 ByteOrder => 'LittleEndian',
799 },
800 },
801 {
802 Name => 'MinoltaTags',
803 Condition => '$$valPt =~ /^MINOLTA DIGITAL CAMERA/',
804 SubDirectory => {
805 TagTable => 'Image::ExifTool::Minolta::MOV2',
806 ByteOrder => 'LittleEndian',
807 },
808 },
809 {
810 Name => 'NikonTags',
811 Condition => '$$valPt =~ /^NIKON DIGITAL CAMERA\0/',
812 SubDirectory => {
813 TagTable => 'Image::ExifTool::Nikon::MOV',
814 ByteOrder => 'LittleEndian',
815 },
816 },
817 {
818 Name => 'OlympusTags1',
819 Condition => '$$valPt =~ /^OLYMPUS DIGITAL CAMERA\0.{9}\x01\0/s',
820 SubDirectory => {
821 TagTable => 'Image::ExifTool::Olympus::MOV1',
822 ByteOrder => 'LittleEndian',
823 },
824 },
825 {
826 Name => 'OlympusTags2',
827 Condition => '$$valPt =~ /^OLYMPUS DIGITAL CAMERA(?!\0.{21}\x0a\0{3})/s',
828 SubDirectory => {
829 TagTable => 'Image::ExifTool::Olympus::MOV2',
830 ByteOrder => 'LittleEndian',
831 },
832 },
833 {
834 Name => 'OlympusTags3',
835 Condition => '$$valPt =~ /^OLYMPUS DIGITAL CAMERA\0/',
836 SubDirectory => {
837 TagTable => 'Image::ExifTool::Olympus::MP4',
838 ByteOrder => 'LittleEndian',
839 },
840 },
841 {
842 Name => 'PentaxTags',
843 Condition => '$$valPt =~ /^PENTAX DIGITAL CAMERA\0/',
844 SubDirectory => {
845 TagTable => 'Image::ExifTool::Pentax::MOV',
846 ByteOrder => 'LittleEndian',
847 },
848 },
849 {
850 Name => 'SamsungTags',
851 Condition => '$$valPt =~ /^SAMSUNG DIGITAL CAMERA\0/',
852 SubDirectory => {
853 TagTable => 'Image::ExifTool::Samsung::MP4',
854 ByteOrder => 'LittleEndian',
855 },
856 },
857 {
858 Name => 'SanyoMOV',
859 Condition => q{
860 $$valPt =~ /^SANYO DIGITAL CAMERA\0/ and
861 $self->{VALUE}->{FileType} eq "MOV"
862 },
863 SubDirectory => {
864 TagTable => 'Image::ExifTool::Sanyo::MOV',
865 ByteOrder => 'LittleEndian',
866 },
867 },
868 {
869 Name => 'SanyoMP4',
870 Condition => q{
871 $$valPt =~ /^SANYO DIGITAL CAMERA\0/ and
872 $self->{VALUE}->{FileType} eq "MP4"
873 },
874 SubDirectory => {
875 TagTable => 'Image::ExifTool::Sanyo::MP4',
876 ByteOrder => 'LittleEndian',
877 },
878 },
879 {
880 Name => 'UnknownTags',
881 Unknown => 1,
882 Binary => 1
883 },
884 ],
885 NCDT => { #PH
886 Name => 'NikonNCDT',
887 SubDirectory => {
888 TagTable => 'Image::ExifTool::Nikon::NCDT',
889 ProcessProc => \&ProcessMOV,
890 },
891 },
892 QVMI => { #PH
893 Name => 'CasioQVMI',
894 # Casio stores standard EXIF-format information in MOV videos (ie. EX-S880)
895 SubDirectory => {
896 TagTable => 'Image::ExifTool::Exif::Main',
897 DirName => 'IFD0',
898 Multi => 0, # (no NextIFD pointer)
899 Start => 10,
900 ByteOrder => 'BigEndian',
901 },
902 },
903 MMA0 => { #PH (DiMage 7Hi)
904 Name => 'MinoltaMMA0',
905 SubDirectory => { TagTable => 'Image::ExifTool::Minolta::MMA' },
906 },
907 MMA1 => { #PH (Dimage A2)
908 Name => 'MinoltaMMA1',
909 SubDirectory => { TagTable => 'Image::ExifTool::Minolta::MMA' },
910 },
911 XMP_ => { #PH (Adobe CS3 Bridge)
912 Name => 'XMP',
913 SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
914 },
915 vndr => 'Vendor', #PH (Samsung PL70)
916 SDLN => 'PlayMode', #PH (NC, Samsung ST80 "SEQ_PLAY")
917 # Canon tags
918 CNCV => 'CompressorVersion', #PH (5D Mark II)
919 CNMN => 'Model', #PH (EOS 550D)
920 CNFV => 'FirmwareVersion', #PH (EOS 550D)
921 CNTH => { #PH (PowerShot S95)
922 Name => 'CanonCNTH',
923 SubDirectory => {
924 TagTable => 'Image::ExifTool::Canon::CNTH',
925 ProcessProc => \&ProcessMOV,
926 },
927 },
928 # CNDB - 2112 bytes (550D)
929 # CNDM - 4 bytes - 0xff,0xd8,0xff,0xd9 (S95)
930 INFO => {
931 Name => 'SamsungINFO',
932 SubDirectory => { TagTable => 'Image::ExifTool::Samsung::INFO' },
933 },
934 FFMV => { #PH (FinePix HS20EXR)
935 Name => 'FujiFilmFFMV',
936 SubDirectory => { TagTable => 'Image::ExifTool::FujiFilm::FFMV' },
937 },
938 MVTG => { #PH (FinePix HS20EXR)
939 Name => 'FujiFilmMVTG',
940 SubDirectory => {
941 TagTable => 'Image::ExifTool::Exif::Main',
942 DirName => 'IFD0',
943 Start => 16,
944 Base => '$start',
945 ByteOrder => 'LittleEndian',
946 },
947 },
948);
949
950# User-specific media data atoms (ref 11)
951%Image::ExifTool::QuickTime::UserMedia = (
952 PROCESS_PROC => \&ProcessMOV,
953 GROUPS => { 2 => 'Video' },
954 MTDT => {
955 Name => 'MetaData',
956 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MetaData' },
957 },
958);
959
960# User-specific media data atoms (ref 11)
961%Image::ExifTool::QuickTime::MetaData = (
962 PROCESS_PROC => \&Image::ExifTool::QuickTime::ProcessMetaData,
963 GROUPS => { 2 => 'Video' },
964 TAG_PREFIX => 'MetaData',
965 0x01 => 'Title',
966 0x03 => {
967 Name => 'ProductionDate',
968 Groups => { 2 => 'Time' },
969 # translate from format "YYYY/mm/dd HH:MM:SS"
970 ValueConv => '$val=~tr{/}{:}; $val',
971 PrintConv => '$self->ConvertDateTime($val)',
972 },
973 0x04 => 'Software',
974 0x05 => 'Product',
975 0x0a => {
976 Name => 'TrackProperty',
977 RawConv => 'my @a=unpack("Nnn",$val); "@a"',
978 PrintConv => [
979 { 0 => 'No presentation', BITMASK => { 0 => 'Main track' } },
980 { 0 => 'No attributes', BITMASK => { 15 => 'Read only' } },
981 '"Priority $val"',
982 ],
983 },
984 0x0b => {
985 Name => 'TimeZone',
986 Groups => { 2 => 'Time' },
987 RawConv => 'Get16s(\$val,0)',
988 PrintConv => 'TimeZoneString($val)',
989 },
990 0x0c => {
991 Name => 'ModifyDate',
992 Groups => { 2 => 'Time' },
993 # translate from format "YYYY/mm/dd HH:MM:SS"
994 ValueConv => '$val=~tr{/}{:}; $val',
995 PrintConv => '$self->ConvertDateTime($val)',
996 },
997);
998
999# Profile atoms (ref 11)
1000%Image::ExifTool::QuickTime::Profile = (
1001 PROCESS_PROC => \&ProcessMOV,
1002 GROUPS => { 2 => 'Video' },
1003 FPRF => {
1004 Name => 'FileGlobalProfile',
1005 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::FileProf' },
1006 },
1007 APRF => {
1008 Name => 'AudioProfile',
1009 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AudioProf' },
1010 },
1011 VPRF => {
1012 Name => 'VideoProfile',
1013 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::VideoProf' },
1014 },
1015);
1016
1017# FPRF atom information (ref 11)
1018%Image::ExifTool::QuickTime::FileProf = (
1019 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1020 GROUPS => { 2 => 'Video' },
1021 FORMAT => 'int32u',
1022 0 => { Name => 'FileProfileVersion', Unknown => 1 }, # unknown = uninteresting
1023 1 => {
1024 Name => 'FileFunctionFlags',
1025 PrintConv => { BITMASK => {
1026 28 => 'Fragmented',
1027 29 => 'Additional tracks',
1028 30 => 'Edited', # (main AV track is edited)
1029 }},
1030 },
1031 # 2 - reserved
1032);
1033
1034# APRF atom information (ref 11)
1035%Image::ExifTool::QuickTime::AudioProf = (
1036 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1037 GROUPS => { 2 => 'Audio' },
1038 FORMAT => 'int32u',
1039 0 => { Name => 'AudioProfileVersion', Unknown => 1 },
1040 1 => 'AudioTrackID',
1041 2 => {
1042 Name => 'AudioCodec',
1043 Format => 'undef[4]',
1044 },
1045 3 => {
1046 Name => 'AudioCodecInfo',
1047 Unknown => 1,
1048 PrintConv => 'sprintf("0x%.4x", $val)',
1049 },
1050 4 => {
1051 Name => 'AudioAttributes',
1052 PrintConv => { BITMASK => {
1053 0 => 'Encrypted',
1054 1 => 'Variable bitrate',
1055 2 => 'Dual mono',
1056 }},
1057 },
1058 5 => {
1059 Name => 'AudioAvgBitrate',
1060 ValueConv => '$val * 1000',
1061 PrintConv => 'ConvertBitrate($val)',
1062 },
1063 6 => {
1064 Name => 'AudioMaxBitrate',
1065 ValueConv => '$val * 1000',
1066 PrintConv => 'ConvertBitrate($val)',
1067 },
1068 7 => 'AudioSampleRate',
1069 8 => 'AudioChannels',
1070);
1071
1072# VPRF atom information (ref 11)
1073%Image::ExifTool::QuickTime::VideoProf = (
1074 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1075 GROUPS => { 2 => 'Video' },
1076 FORMAT => 'int32u',
1077 0 => { Name => 'VideoProfileVersion', Unknown => 1 },
1078 1 => 'VideoTrackID',
1079 2 => {
1080 Name => 'VideoCodec',
1081 Format => 'undef[4]',
1082 },
1083 3 => {
1084 Name => 'VideoCodecInfo',
1085 Unknown => 1,
1086 PrintConv => 'sprintf("0x%.4x", $val)',
1087 },
1088 4 => {
1089 Name => 'VideoAttributes',
1090 PrintConv => { BITMASK => {
1091 0 => 'Encrypted',
1092 1 => 'Variable bitrate',
1093 2 => 'Variable frame rate',
1094 3 => 'Interlaced',
1095 }},
1096 },
1097 5 => {
1098 Name => 'VideoAvgBitrate',
1099 ValueConv => '$val * 1000',
1100 PrintConv => 'ConvertBitrate($val)',
1101 },
1102 6 => {
1103 Name => 'VideoMaxBitrate',
1104 ValueConv => '$val * 1000',
1105 PrintConv => 'ConvertBitrate($val)',
1106 },
1107 7 => {
1108 Name => 'VideoAvgFrameRate',
1109 Format => 'fixed32u',
1110 PrintConv => 'int($val * 1000 + 0.5) / 1000',
1111 },
1112 8 => {
1113 Name => 'VideoMaxFrameRate',
1114 Format => 'fixed32u',
1115 PrintConv => 'int($val * 1000 + 0.5) / 1000',
1116 },
1117 9 => {
1118 Name => 'VideoSize',
1119 Format => 'int16u[2]',
1120 PrintConv => '$val=~tr/ /x/; $val',
1121 },
1122 10 => {
1123 Name => 'PixelAspectRatio',
1124 Format => 'int16u[2]',
1125 PrintConv => '$val=~tr/ /:/; $val',
1126 },
1127);
1128
1129# meta atoms
1130%Image::ExifTool::QuickTime::Meta = (
1131 PROCESS_PROC => \&ProcessMOV,
1132 GROUPS => { 2 => 'Video' },
1133 ilst => {
1134 Name => 'ItemList',
1135 SubDirectory => {
1136 TagTable => 'Image::ExifTool::QuickTime::ItemList',
1137 HasData => 1, # process atoms as containers with 'data' elements
1138 },
1139 },
1140 # MP4 tags (ref 5)
1141 hdlr => {
1142 Name => 'Handler',
1143 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Handler' },
1144 },
1145 dinf => {
1146 Name => 'DataInformation',
1147 Flags => ['Binary','Unknown'],
1148 },
1149 ipmc => {
1150 Name => 'IPMPControl',
1151 Flags => ['Binary','Unknown'],
1152 },
1153 iloc => {
1154 Name => 'ItemLocation',
1155 Flags => ['Binary','Unknown'],
1156 },
1157 ipro => {
1158 Name => 'ItemProtection',
1159 Flags => ['Binary','Unknown'],
1160 },
1161 iinf => {
1162 Name => 'ItemInformation',
1163 Flags => ['Binary','Unknown'],
1164 },
1165 'xml ' => {
1166 Name => 'XML',
1167 SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
1168 },
1169 'keys' => {
1170 Name => 'Keys',
1171 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Keys' },
1172 },
1173 bxml => {
1174 Name => 'BinaryXML',
1175 Flags => ['Binary','Unknown'],
1176 },
1177 pitm => {
1178 Name => 'PrimaryItemReference',
1179 Flags => ['Binary','Unknown'],
1180 },
1181 free => { #PH
1182 Name => 'Free',
1183 Flags => ['Binary','Unknown'],
1184 },
1185);
1186
1187# track reference atoms
1188%Image::ExifTool::QuickTime::TrackRef = (
1189 PROCESS_PROC => \&ProcessMOV,
1190 GROUPS => { 2 => 'Video' },
1191 chap => { Name => 'ChapterList', Format => 'int32u' },
1192 # also: tmcd, sync, scpt, ssrc, iTunesInfo
1193);
1194
1195# item list atoms
1196# -> these atoms are unique, and contain one or more 'data' atoms
1197%Image::ExifTool::QuickTime::ItemList = (
1198 PROCESS_PROC => \&ProcessMOV,
1199 GROUPS => { 2 => 'Audio' },
1200 NOTES => q{
1201 As well as these tags, the 'mdta' handler uses numerical tag ID's which are
1202 added dynamically to this table after processing the Meta Keys information.
1203 },
1204 # in this table, binary 1 and 2-byte "data"-type tags are interpreted as
1205 # int8u and int16u. Multi-byte binary "data" tags are extracted as binary data
1206 "\xa9ART" => 'Artist',
1207 "\xa9alb" => 'Album',
1208 "\xa9cmt" => 'Comment',
1209 "\xa9com" => 'Composer',
1210 "\xa9day" => { Name => 'Year', Groups => { 2 => 'Time' } },
1211 "\xa9des" => 'Description', #4
1212 "\xa9enc" => 'EncodedBy', #10
1213 "\xa9gen" => 'Genre',
1214 "\xa9grp" => 'Grouping',
1215 "\xa9lyr" => 'Lyrics',
1216 "\xa9nam" => 'Title',
1217 # "\xa9st3" ? #10
1218 "\xa9too" => 'Encoder',
1219 "\xa9trk" => 'Track',
1220 "\xa9wrt" => 'Composer',
1221 '----' => {
1222 Name => 'iTunesInfo',
1223 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::iTunesInfo' },
1224 },
1225 aART => 'AlbumArtist',
1226 covr => 'CoverArt',
1227 cpil => { #10
1228 Name => 'Compilation',
1229 PrintConv => { 0 => 'No', 1 => 'Yes' },
1230 },
1231 disk => {
1232 Name => 'DiskNumber',
1233 ValueConv => 'length($val) >= 6 ? join(" of ",unpack("x2nn",$val)) : \$val',
1234 },
1235 pgap => { #10
1236 Name => 'PlayGap',
1237 PrintConv => {
1238 0 => 'Insert Gap',
1239 1 => 'No Gap',
1240 },
1241 },
1242 tmpo => {
1243 Name => 'BeatsPerMinute',
1244 Format => 'int16u', # marked as boolean but really int16u in my sample
1245 },
1246 trkn => {
1247 Name => 'TrackNumber',
1248 ValueConv => 'length($val) >= 6 ? join(" of ",unpack("x2nn",$val)) : \$val',
1249 },
1250#
1251# Note: it is possible that the tags below are not being decoded properly
1252# because I don't have samples to verify many of these - PH
1253#
1254 akID => { #10
1255 Name => 'AppleStoreAccountType',
1256 PrintConv => {
1257 0 => 'iTunes',
1258 1 => 'AOL',
1259 },
1260 },
1261 albm => 'Album', #(ffmpeg source)
1262 apID => 'AppleStoreAccount',
1263 atID => { #10 (or TV series)
1264 Name => 'AlbumTitleID',
1265 Format => 'int32u',
1266 },
1267 auth => { Name => 'Author', Groups => { 2 => 'Author' } },
1268 catg => 'Category', #7
1269 cnID => { #10
1270 Name => 'AppleStoreCatalogID',
1271 Format => 'int32u',
1272 },
1273 cprt => { Name => 'Copyright', Groups => { 2 => 'Author' } },
1274 dscp => 'Description',
1275 desc => 'Description', #7
1276 gnre => { #10
1277 Name => 'Genre',
1278 PrintConv => q{
1279 return $val unless $val =~ /^\d+$/;
1280 require Image::ExifTool::ID3;
1281 Image::ExifTool::ID3::PrintGenre($val - 1); # note the "- 1"
1282 },
1283 },
1284 egid => 'EpisodeGlobalUniqueID', #7
1285 geID => { #10
1286 Name => 'GenreID',
1287 Format => 'int32u',
1288 # 4005 = Kids
1289 # 4010 = Teens
1290 },
1291 grup => 'Grouping', #10
1292 hdvd => { #10
1293 Name => 'HDVideo',
1294 PrintConv => { 0 => 'No', 1 => 'Yes' },
1295 },
1296 keyw => 'Keyword', #7
1297 ldes => 'LongDescription', #10
1298 pcst => { #7
1299 Name => 'Podcast',
1300 PrintConv => { 0 => 'No', 1 => 'Yes' },
1301 },
1302 perf => 'Performer',
1303 plID => { #10 (or TV season)
1304 Name => 'PlayListID',
1305 Format => 'int8u', # actually int64u, but split it up
1306 },
1307 purd => 'PurchaseDate', #7
1308 purl => 'PodcastURL', #7
1309 rtng => { #10
1310 Name => 'Rating',
1311 PrintConv => {
1312 0 => 'none',
1313 2 => 'Clean',
1314 4 => 'Explicit',
1315 },
1316 },
1317 sfID => { #10
1318 Name => 'AppleStoreCountry',
1319 Format => 'int32u',
1320 PrintConv => {
1321 143460 => 'Australia',
1322 143445 => 'Austria',
1323 143446 => 'Belgium',
1324 143455 => 'Canada',
1325 143458 => 'Denmark',
1326 143447 => 'Finland',
1327 143442 => 'France',
1328 143443 => 'Germany',
1329 143448 => 'Greece',
1330 143449 => 'Ireland',
1331 143450 => 'Italy',
1332 143462 => 'Japan',
1333 143451 => 'Luxembourg',
1334 143452 => 'Netherlands',
1335 143461 => 'New Zealand',
1336 143457 => 'Norway',
1337 143453 => 'Portugal',
1338 143454 => 'Spain',
1339 143456 => 'Sweden',
1340 143459 => 'Switzerland',
1341 143444 => 'United Kingdom',
1342 143441 => 'United States',
1343 },
1344 },
1345 soaa => 'SortAlbumArtist', #10
1346 soal => 'SortAlbum', #10
1347 soar => 'SortArtist', #10
1348 soco => 'SortComposer', #10
1349 sonm => 'SortName', #10
1350 sosn => 'SortShow', #10
1351 stik => { #10
1352 Name => 'MediaType',
1353 PrintConv => { #(http://weblog.xanga.com/gryphondwb/615474010/iphone-ringtones---what-did-itunes-741-really-do.html)
1354 0 => 'Movie',
1355 1 => 'Normal (Music)',
1356 2 => 'Audiobook',
1357 5 => 'Whacked Bookmark',
1358 6 => 'Music Video',
1359 9 => 'Short Film',
1360 10 => 'TV Show',
1361 11 => 'Booklet',
1362 14 => 'Ringtone',
1363 },
1364 },
1365 titl => 'Title',
1366 tven => 'TVEpisodeID', #7
1367 tves => { #7/10
1368 Name => 'TVEpisode',
1369 Format => 'int32u',
1370 },
1371 tvnn => 'TVNetworkName', #7
1372 tvsh => 'TVShow', #10
1373 tvsn => { #7/10
1374 Name => 'TVSeason',
1375 Format => 'int32u',
1376 },
1377 yrrc => 'Year', #(ffmpeg source)
1378);
1379
1380# item list keys (ref PH)
1381%Image::ExifTool::QuickTime::Keys = (
1382 PROCESS_PROC => \&Image::ExifTool::QuickTime::ProcessKeys,
1383 NOTES => q{
1384 This directory contains a list of key names which are used to decode
1385 ItemList tags written by the "mdta" handler. The prefix of
1386 "com.apple.quicktime." has been removed from all TagID's below.
1387 },
1388 version => 'Version',
1389 album => 'Album',
1390 artist => { },
1391 artwork => { },
1392 author => { Name => 'Author', Groups => { 2 => 'Author' } },
1393 comment => { },
1394 copyright => { Name => 'Copyright', Groups => { 2 => 'Author' } },
1395 creationdate=> {
1396 Name => 'CreationDate',
1397 Groups => { 2 => 'Time' },
1398 ValueConv => q{
1399 require Image::ExifTool::XMP;
1400 $val = Image::ExifTool::XMP::ConvertXMPDate($val,1);
1401 $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
1402 return $val;
1403 },
1404 PrintConv => '$self->ConvertDateTime($val)',
1405 },
1406 description => { },
1407 director => { },
1408 genre => { },
1409 information => { },
1410 keywords => { },
1411 make => { Name => 'Make', Groups => { 2 => 'Camera' } },
1412 model => { Name => 'Model', Groups => { 2 => 'Camera' } },
1413 publisher => { },
1414 software => { },
1415 year => { Groups => { 2 => 'Time' } },
1416 'camera.identifier' => 'CameraIdentifier', # (iPhone 4)
1417 'camera.framereadouttimeinmicroseconds' => { # (iPhone 4)
1418 Name => 'FrameReadoutTime',
1419 ValueConv => '$val * 1e-6',
1420 PrintConv => '$val * 1e6 . " microseconds"',
1421 },
1422 'location.ISO6709' => {
1423 Name => 'GPSCoordinates',
1424 Groups => { 2 => 'Location' },
1425 ValueConv => \&ConvertISO6709,
1426 PrintConv => \&PrintGPSCoordinates,
1427 },
1428 'location.name' => { Name => 'LocationName', Groups => { 2 => 'Location' } },
1429 'location.body' => { Name => 'LocationBody', Groups => { 2 => 'Location' } },
1430 'location.note' => { Name => 'LocationNote', Groups => { 2 => 'Location' } },
1431 'location.role' => {
1432 Name => 'LocationRole',
1433 Groups => { 2 => 'Location' },
1434 PrintConv => {
1435 0 => 'Shooting Location',
1436 1 => 'Real Location',
1437 2 => 'Fictional Location',
1438 },
1439 },
1440 'location.date' => {
1441 Name => 'LocationDate',
1442 Groups => { 2 => 'Time' },
1443 ValueConv => q{
1444 require Image::ExifTool::XMP;
1445 $val = Image::ExifTool::XMP::ConvertXMPDate($val);
1446 $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
1447 return $val;
1448 },
1449 PrintConv => '$self->ConvertDateTime($val)',
1450 },
1451 'direction.facing' => { Name => 'CameraDirection', Groups => { 2 => 'Location' } },
1452 'direction.motion' => { Name => 'CameraMotion', Groups => { 2 => 'Location' } },
1453 'location.body' => { Name => 'LocationBody', Groups => { 2 => 'Location' } },
1454 'player.version' => 'PlayerVersion',
1455 'player.movie.visual.brightness'=> 'Brightness',
1456 'player.movie.visual.color' => 'Color',
1457 'player.movie.visual.tint' => 'Tint',
1458 'player.movie.visual.contrast' => 'Contrast',
1459 'player.movie.audio.gain' => 'AudioGain',
1460 'player.movie.audio.treble' => 'Trebel',
1461 'player.movie.audio.bass' => 'Bass',
1462 'player.movie.audio.balance' => 'Balance',
1463 'player.movie.audio.pitchshift' => 'PitchShift',
1464 'player.movie.audio.mute' => {
1465 Name => 'Mute',
1466 Format => 'int8u',
1467 PrintConv => { 0 => 'Off', 1 => 'On' },
1468 },
1469);
1470
1471# iTunes info ('----') atoms
1472%Image::ExifTool::QuickTime::iTunesInfo = (
1473 PROCESS_PROC => \&ProcessMOV,
1474 GROUPS => { 2 => 'Audio' },
1475 # 'mean'/'name'/'data' atoms form a triplet, but unfortunately
1476 # I can't find any source for decoding 'data'.
1477 # 'mean' is normally 'com.apple.iTunes'
1478 # 'name' values: 'tool', 'iTunNORM' (volume normalization),
1479 # 'iTunSMPB', 'iTunes_CDDB_IDs', 'iTunes_CDDB_TrackNumber'
1480 mean => {
1481 Name => 'Mean',
1482 Unknown => 1,
1483 },
1484 name => {
1485 Name => 'Name',
1486 Unknown => 1,
1487 },
1488 data => {
1489 Name => 'Data',
1490 Flags => ['Binary','Unknown'],
1491 },
1492);
1493
1494# print to video data block
1495%Image::ExifTool::QuickTime::Video = (
1496 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1497 GROUPS => { 2 => 'Video' },
1498 0 => {
1499 Name => 'DisplaySize',
1500 PrintConv => {
1501 0 => 'Normal',
1502 1 => 'Double Size',
1503 2 => 'Half Size',
1504 3 => 'Full Screen',
1505 4 => 'Current Size',
1506 },
1507 },
1508 6 => {
1509 Name => 'SlideShow',
1510 PrintConv => {
1511 0 => 'No',
1512 1 => 'Yes',
1513 },
1514 },
1515);
1516
1517# 'hnti' atoms
1518%Image::ExifTool::QuickTime::HintInfo = (
1519 PROCESS_PROC => \&ProcessMOV,
1520 GROUPS => { 2 => 'Video' },
1521 'rtp ' => {
1522 Name => 'RealtimeStreamingProtocol',
1523 PrintConv => '$val=~s/^sdp /(SDP) /; $val',
1524 },
1525 'sdp ' => 'StreamingDataProtocol',
1526);
1527
1528# 'hinf' atoms
1529%Image::ExifTool::QuickTime::HintTrackInfo = (
1530 PROCESS_PROC => \&ProcessMOV,
1531 GROUPS => { 2 => 'Video' },
1532 trpY => { Name => 'TotalBytes', Format => 'int64u' }, #(documented)
1533 trpy => { Name => 'TotalBytes', Format => 'int64u' }, #(observed)
1534 totl => { Name => 'TotalBytes', Format => 'int32u' },
1535 nump => { Name => 'NumPackets', Format => 'int64u' },
1536 npck => { Name => 'NumPackets', Format => 'int32u' },
1537 tpyl => { Name => 'TotalBytesNoRTPHeaders', Format => 'int64u' },
1538 tpaY => { Name => 'TotalBytesNoRTPHeaders', Format => 'int32u' }, #(documented)
1539 tpay => { Name => 'TotalBytesNoRTPHeaders', Format => 'int32u' }, #(observed)
1540 maxr => {
1541 Name => 'MaxDataRate',
1542 Format => 'int32u',
1543 Count => 2,
1544 PrintConv => 'my @a=split(" ",$val);sprintf("%d bytes in %.3f s",$a[1],$a[0]/1000)',
1545 },
1546 dmed => { Name => 'MediaTrackBytes', Format => 'int64u' },
1547 dimm => { Name => 'ImmediateDataBytes', Format => 'int64u' },
1548 drep => { Name => 'RepeatedDataBytes', Format => 'int64u' },
1549 tmin => {
1550 Name => 'MinTransmissionTime',
1551 Format => 'int32u',
1552 PrintConv => 'sprintf("%.3f s",$val/1000)',
1553 },
1554 tmax => {
1555 Name => 'MaxTransmissionTime',
1556 Format => 'int32u',
1557 PrintConv => 'sprintf("%.3f s",$val/1000)',
1558 },
1559 pmax => { Name => 'LargestPacketSize', Format => 'int32u' },
1560 dmax => {
1561 Name => 'LargestPacketDuration',
1562 Format => 'int32u',
1563 PrintConv => 'sprintf("%.3f s",$val/1000)',
1564 },
1565 payt => {
1566 Name => 'PayloadType',
1567 ValueConv => 'unpack("N",$val) . " " . substr($val, 5)',
1568 PrintConv => '$val=~s/ /, /;$val',
1569 },
1570);
1571
1572# Kodak DcMD atoms (ref PH)
1573%Image::ExifTool::QuickTime::DcMD = (
1574 PROCESS_PROC => \&ProcessMOV,
1575 GROUPS => { 2 => 'Video' },
1576 NOTES => 'Metadata directory found in MOV videos from some Kodak cameras.',
1577 Cmbo => {
1578 Name => 'CameraByteOrder',
1579 PrintConv => {
1580 II => 'Little-endian (Intel, II)',
1581 MM => 'Big-endian (Motorola, MM)',
1582 },
1583 },
1584 DcME => {
1585 Name => 'DcME',
1586 SubDirectory => {
1587 TagTable => 'Image::ExifTool::QuickTime::DcME',
1588 },
1589 },
1590);
1591
1592# Kodak DcME atoms (ref PH)
1593%Image::ExifTool::QuickTime::DcME = (
1594 PROCESS_PROC => \&ProcessMOV,
1595 GROUPS => { 2 => 'Video' },
1596 # Mtmd = binary data ("00 00 00 00 00 00 00 01" x 3)
1597 # Keyw = keywords? (six bytes all zero)
1598 # Rate = 2 bytes "00 00"
1599);
1600
1601# MP4 media box (ref 5)
1602%Image::ExifTool::QuickTime::Media = (
1603 PROCESS_PROC => \&ProcessMOV,
1604 GROUPS => { 2 => 'Video' },
1605 NOTES => 'MP4 media box.',
1606 mdhd => {
1607 Name => 'MediaHeader',
1608 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MediaHeader' },
1609 },
1610 hdlr => {
1611 Name => 'Handler',
1612 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Handler' },
1613 },
1614 minf => {
1615 Name => 'MediaInfo',
1616 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MediaInfo' },
1617 },
1618);
1619
1620# MP4 media header box (ref 5)
1621%Image::ExifTool::QuickTime::MediaHeader = (
1622 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1623 GROUPS => { 2 => 'Video' },
1624 FORMAT => 'int32u',
1625 0 => {
1626 Name => 'MediaHeaderVersion',
1627 RawConv => '$$self{MediaHeaderVersion} = $val',
1628 },
1629 1 => {
1630 Name => 'MediaCreateDate',
1631 Groups => { 2 => 'Time' },
1632 %timeInfo,
1633 # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
1634 Hook => '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
1635 },
1636 2 => {
1637 Name => 'MediaModifyDate',
1638 Groups => { 2 => 'Time' },
1639 %timeInfo,
1640 # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
1641 Hook => '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
1642 },
1643 3 => {
1644 Name => 'MediaTimeScale',
1645 RawConv => '$$self{MediaTS} = $val',
1646 },
1647 4 => {
1648 Name => 'MediaDuration',
1649 RawConv => '$$self{MediaTS} ? $val / $$self{MediaTS} : $val',
1650 PrintConv => '$$self{MediaTS} ? ConvertDuration($val) : $val',
1651 # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
1652 Hook => '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
1653 },
1654 5 => {
1655 Name => 'MediaLanguageCode',
1656 Format => 'int16u',
1657 RawConv => '$val ? $val : undef',
1658 # allow both Macintosh (for MOV files) and ISO (for MP4 files) language codes
1659 ValueConv => '$val < 0x400 ? $val : pack "C*", map { (($val>>$_)&0x1f)+0x60 } 10, 5, 0',
1660 PrintConv => q{
1661 return $val unless $val =~ /^\d+$/;
1662 require Image::ExifTool::Font;
1663 return $Image::ExifTool::Font::ttLang{Macintosh}{$val} || "Unknown ($val)";
1664 },
1665 },
1666);
1667
1668# MP4 media information box (ref 5)
1669%Image::ExifTool::QuickTime::MediaInfo = (
1670 PROCESS_PROC => \&ProcessMOV,
1671 GROUPS => { 2 => 'Video' },
1672 NOTES => 'MP4 media info box.',
1673 vmhd => {
1674 Name => 'VideoHeader',
1675 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::VideoHeader' },
1676 },
1677 smhd => {
1678 Name => 'AudioHeader',
1679 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AudioHeader' },
1680 },
1681 hmhd => {
1682 Name => 'HintHeader',
1683 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HintHeader' },
1684 },
1685 nmhd => {
1686 Name => 'NullMediaHeader',
1687 Flags => ['Binary','Unknown'],
1688 },
1689 dinf => {
1690 Name => 'DataInfo',
1691 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::DataInfo' },
1692 },
1693 gmhd => {
1694 Name => 'GenMediaHeader',
1695 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::GenMediaHeader' },
1696 },
1697 hdlr => { #PH
1698 Name => 'Handler',
1699 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Handler' },
1700 },
1701 stbl => {
1702 Name => 'SampleTable',
1703 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SampleTable' },
1704 },
1705);
1706
1707# MP4 video media header (ref 5)
1708%Image::ExifTool::QuickTime::VideoHeader = (
1709 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1710 GROUPS => { 2 => 'Video' },
1711 NOTES => 'MP4 video media header.',
1712 FORMAT => 'int16u',
1713 2 => {
1714 Name => 'GraphicsMode',
1715 PrintHex => 1,
1716 SeparateTable => 'GraphicsMode',
1717 PrintConv => \%graphicsMode,
1718 },
1719 3 => { Name => 'OpColor', Format => 'int16u[3]' },
1720);
1721
1722# MP4 audio media header (ref 5)
1723%Image::ExifTool::QuickTime::AudioHeader = (
1724 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1725 GROUPS => { 2 => 'Audio' },
1726 NOTES => 'MP4 audio media header.',
1727 FORMAT => 'int16u',
1728 2 => { Name => 'Balance', Format => 'fixed16s' },
1729);
1730
1731# MP4 hint media header (ref 5)
1732%Image::ExifTool::QuickTime::HintHeader = (
1733 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1734 NOTES => 'MP4 hint media header.',
1735 FORMAT => 'int16u',
1736 2 => 'MaxPDUSize',
1737 3 => 'AvgPDUSize',
1738 4 => { Name => 'MaxBitrate', Format => 'int32u', PrintConv => 'ConvertBitrate($val)' },
1739 6 => { Name => 'AvgBitrate', Format => 'int32u', PrintConv => 'ConvertBitrate($val)' },
1740);
1741
1742# MP4 sample table box (ref 5)
1743%Image::ExifTool::QuickTime::SampleTable = (
1744 PROCESS_PROC => \&ProcessMOV,
1745 GROUPS => { 2 => 'Video' },
1746 NOTES => 'MP4 sample table box.',
1747 stsd => [
1748 {
1749 Name => 'AudioSampleDesc',
1750 Condition => '$$self{HandlerType} and $$self{HandlerType} eq "soun"',
1751 SubDirectory => {
1752 TagTable => 'Image::ExifTool::QuickTime::AudioSampleDesc',
1753 Start => 8, # skip version number and count
1754 },
1755 },{
1756 Name => 'VideoSampleDesc',
1757 Condition => '$$self{HandlerType} and $$self{HandlerType} eq "vide"',
1758 SubDirectory => {
1759 TagTable => 'Image::ExifTool::QuickTime::ImageDesc',
1760 Start => 8, # skip version number and count
1761 },
1762 },{
1763 Name => 'HintSampleDesc',
1764 Condition => '$$self{HandlerType} and $$self{HandlerType} eq "hint"',
1765 SubDirectory => {
1766 TagTable => 'Image::ExifTool::QuickTime::HintSampleDesc',
1767 Start => 8, # skip version number and count
1768 },
1769 },{
1770 Name => 'OtherSampleDesc',
1771 SubDirectory => {
1772 TagTable => 'Image::ExifTool::QuickTime::OtherSampleDesc',
1773 Start => 8, # skip version number and count
1774 },
1775 },
1776 # (Note: "alis" HandlerType handled by the parent audio or video handler)
1777 ],
1778 stts => [ # decoding time-to-sample table
1779 {
1780 Name => 'VideoFrameRate',
1781 Notes => 'average rate calculated from time-to-sample table for video media',
1782 Condition => '$$self{HandlerType} and $$self{HandlerType} eq "vide"',
1783 # (must be RawConv so appropriate MediaTS is used in calculation)
1784 RawConv => 'Image::ExifTool::QuickTime::CalcSampleRate($self, \$val)',
1785 PrintConv => 'int($val * 1000 + 0.5) / 1000',
1786 },
1787 {
1788 Name => 'TimeToSampleTable',
1789 Flags => ['Binary','Unknown'],
1790 },
1791 ],
1792 ctts => {
1793 Name => 'CompositionTimeToSample',
1794 Flags => ['Binary','Unknown'],
1795 },
1796 stsc => {
1797 Name => 'SampleToChunk',
1798 Flags => ['Binary','Unknown'],
1799 },
1800 stsz => {
1801 Name => 'SampleSizes',
1802 Flags => ['Binary','Unknown'],
1803 },
1804 stz2 => {
1805 Name => 'CompactSampleSizes',
1806 Flags => ['Binary','Unknown'],
1807 },
1808 stco => {
1809 Name => 'ChunkOffset',
1810 Flags => ['Binary','Unknown'],
1811 },
1812 co64 => {
1813 Name => 'ChunkOffset64',
1814 Flags => ['Binary','Unknown'],
1815 },
1816 stss => {
1817 Name => 'SyncSampleTable',
1818 Flags => ['Binary','Unknown'],
1819 },
1820 stsh => {
1821 Name => 'ShadowSyncSampleTable',
1822 Flags => ['Binary','Unknown'],
1823 },
1824 padb => {
1825 Name => 'SamplePaddingBits',
1826 Flags => ['Binary','Unknown'],
1827 },
1828 stdp => {
1829 Name => 'SampleDegradationPriority',
1830 Flags => ['Binary','Unknown'],
1831 },
1832 sdtp => {
1833 Name => 'IdependentAndDisposableSamples',
1834 Flags => ['Binary','Unknown'],
1835 },
1836 sbgp => {
1837 Name => 'SampleToGroup',
1838 Flags => ['Binary','Unknown'],
1839 },
1840 sgpd => {
1841 Name => 'SampleGroupDescription',
1842 Flags => ['Binary','Unknown'],
1843 },
1844 subs => {
1845 Name => 'Sub-sampleInformation',
1846 Flags => ['Binary','Unknown'],
1847 },
1848 cslg => {
1849 Name => 'CompositionToDecodeTimelineMapping',
1850 Flags => ['Binary','Unknown'],
1851 },
1852);
1853
1854# MP4 audio sample description box (ref 5)
1855%Image::ExifTool::QuickTime::AudioSampleDesc = (
1856 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1857 GROUPS => { 2 => 'Audio' },
1858 FORMAT => 'int16u',
1859 NOTES => 'MP4 audio sample description.',
1860 2 => {
1861 Name => 'AudioFormat',
1862 Format => 'undef[4]',
1863 RawConv => q{
1864 return undef unless $val =~ /^[\w ]{4}$/i;
1865 # check for protected audio format
1866 $self->OverrideFileType('M4P') if $val eq 'drms' and $$self{VALUE}{FileType} eq 'M4A';
1867 return $val;
1868 },
1869 },
1870 10 => { #PH
1871 Name => 'AudioVendorID',
1872 Format => 'undef[4]',
1873 RawConv => '$val eq "\0\0\0\0" ? undef : $val',
1874 PrintConv => \%vendorID,
1875 SeparateTable => 'VendorID',
1876 },
1877 12 => 'AudioChannels',
1878 13 => 'AudioBitsPerSample',
1879 16 => { Name => 'AudioSampleRate', Format => 'fixed32u' },
1880 28 => { #PH
1881 Name => 'AudioFormat',
1882 Format => 'undef[4]',
1883 RawConv => '$val =~ /^[\w ]{4}$/i ? $val : undef',
1884 Notes => 'in Casio MOV videos',
1885 },
1886);
1887
1888# MP4 hint sample description box (ref 5)
1889%Image::ExifTool::QuickTime::HintSampleDesc = (
1890 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1891 FORMAT => 'int16u',
1892 NOTES => 'MP4 hint sample description.',
1893 2 => { Name => 'HintFormat', Format => 'undef[4]' },
1894);
1895
1896# MP4 generic sample description box
1897%Image::ExifTool::QuickTime::OtherSampleDesc = (
1898 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1899 FORMAT => 'int16u',
1900 2 => { Name => 'OtherFormat', Format => 'undef[4]' },
1901);
1902
1903# MP4 data information box (ref 5)
1904%Image::ExifTool::QuickTime::DataInfo = (
1905 PROCESS_PROC => \&ProcessMOV,
1906 NOTES => 'MP4 data information box.',
1907 dref => {
1908 Name => 'DataRef',
1909 SubDirectory => {
1910 TagTable => 'Image::ExifTool::QuickTime::DataRef',
1911 Start => 8,
1912 },
1913 },
1914);
1915
1916# Generic media header
1917%Image::ExifTool::QuickTime::GenMediaHeader = (
1918 PROCESS_PROC => \&ProcessMOV,
1919 gmin => {
1920 Name => 'GenMediaInfo',
1921 SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::GenMediaInfo' },
1922 },
1923 text => {
1924 Name => 'Text',
1925 Flags => ['Binary','Unknown'],
1926 },
1927);
1928
1929# Generic media info (ref http://sourceforge.jp/cvs/view/ntvrec/ntvrec/libqtime/gmin.h?view=co)
1930%Image::ExifTool::QuickTime::GenMediaInfo = (
1931 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1932 GROUPS => { 2 => 'Video' },
1933 0 => 'GenMediaVersion',
1934 1 => { Name => 'GenFlags', Format => 'int8u[3]' },
1935 4 => { Name => 'GenGraphicsMode',
1936 Format => 'int16u',
1937 PrintHex => 1,
1938 SeparateTable => 'GraphicsMode',
1939 PrintConv => \%graphicsMode,
1940 },
1941 6 => { Name => 'GenOpColor', Format => 'int16u[3]' },
1942 12 => { Name => 'GenBalance', Format => 'fixed16s' },
1943);
1944
1945# MP4 data reference box (ref 5)
1946%Image::ExifTool::QuickTime::DataRef = (
1947 PROCESS_PROC => \&ProcessMOV,
1948 NOTES => 'MP4 data reference box.',
1949 'url ' => {
1950 Name => 'URL',
1951 RawConv => q{
1952 # ignore if self-contained (flags bit 0 set)
1953 return undef if unpack("N",$val) & 0x01;
1954 $_ = substr($val,4); s/\0.*//s; $_;
1955 },
1956 },
1957 'urn ' => {
1958 Name => 'URN',
1959 RawConv => q{
1960 return undef if unpack("N",$val) & 0x01;
1961 $_ = substr($val,4); s/\0.*//s; $_;
1962 },
1963 },
1964);
1965
1966# MP4 handler box (ref 5)
1967%Image::ExifTool::QuickTime::Handler = (
1968 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1969 GROUPS => { 2 => 'Video' },
1970 4 => { #PH
1971 Name => 'HandlerClass',
1972 Format => 'undef[4]',
1973 RawConv => '$val eq "\0\0\0\0" ? undef : $val',
1974 PrintConv => {
1975 mhlr => 'Media Handler',
1976 dhlr => 'Data Handler',
1977 },
1978 },
1979 8 => {
1980 Name => 'HandlerType',
1981 Format => 'undef[4]',
1982 RawConv => '$$self{HandlerType} = $val unless $val eq "alis"; $val',
1983 PrintConv => {
1984 alis => 'Alias Data', #PH
1985 crsm => 'Clock Reference', #3
1986 hint => 'Hint Track',
1987 ipsm => 'IPMP', #3
1988 m7sm => 'MPEG-7 Stream', #3
1989 mdir => 'Metadata', #3
1990 mdta => 'Metadata Tags', #PH
1991 mjsm => 'MPEG-J', #3
1992 ocsm => 'Object Content', #3
1993 odsm => 'Object Descriptor', #3
1994 sdsm => 'Scene Description', #3
1995 soun => 'Audio Track',
1996 text => 'Text', #PH (but what type? subtitle?)
1997 'url '=> 'URL', #3
1998 vide => 'Video Track',
1999 },
2000 },
2001 12 => { #PH
2002 Name => 'HandlerVendorID',
2003 Format => 'undef[4]',
2004 RawConv => '$val eq "\0\0\0\0" ? undef : $val',
2005 PrintConv => \%vendorID,
2006 SeparateTable => 'VendorID',
2007 },
2008 24 => {
2009 Name => 'HandlerDescription',
2010 Format => 'string',
2011 # (sometimes this is a Pascal string, and sometimes it is a C string)
2012 RawConv => q{
2013 $val=substr($val,1,ord($1)) if $val=~/^([\0-\x1f])/ and ord($1)<length($val);
2014 length $val ? $val : undef;
2015 },
2016 },
2017);
2018
2019# Flip uuid data (ref PH)
2020%Image::ExifTool::QuickTime::Flip = (
2021 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2022 FORMAT => 'int32u',
2023 FIRST_ENTRY => 0,
2024 NOTES => 'Found in MP4 files from Flip Video cameras.',
2025 GROUPS => { 1 => 'MakerNotes', 2 => 'Image' },
2026 1 => 'PreviewImageWidth',
2027 2 => 'PreviewImageHeight',
2028 13 => 'PreviewImageLength',
2029 14 => { # (confirmed for FlipVideoMinoHD)
2030 Name => 'SerialNumber',
2031 Groups => { 2 => 'Camera' },
2032 Format => 'string[16]',
2033 },
2034 28 => {
2035 Name => 'PreviewImage',
2036 Format => 'undef[$val{13}]',
2037 RawConv => '$self->ValidateImage(\$val, $tag)',
2038 },
2039);
2040
2041# QuickTime composite tags
2042%Image::ExifTool::QuickTime::Composite = (
2043 GROUPS => { 2 => 'Video' },
2044 Rotation => {
2045 Require => {
2046 0 => 'QuickTime:MatrixStructure',
2047 1 => 'QuickTime:HandlerType',
2048 },
2049 ValueConv => 'Image::ExifTool::QuickTime::CalcRotation($self)',
2050 },
2051 AvgBitrate => {
2052 Priority => 0, # let QuickTime::AvgBitrate take priority
2053 Require => {
2054 0 => 'QuickTime::MovieDataSize',
2055 1 => 'QuickTime::Duration',
2056 },
2057 RawConv => q{
2058 return undef unless $val[1];
2059 $val[1] /= $$self{TimeScale} if $$self{TimeScale};
2060 return int($val[0] * 8 / $val[1] + 0.5);
2061 },
2062 PrintConv => 'ConvertBitrate($val)',
2063 },
2064 GPSLatitude => {
2065 Require => 'QuickTime:GPSCoordinates',
2066 Groups => { 2 => 'Location' },
2067 ValueConv => 'my @c = split " ", $val; $c[0]',
2068 PrintConv => q{
2069 require Image::ExifTool::GPS;
2070 Image::ExifTool::GPS::ToDMS($self, $val, 1, 'N');
2071 },
2072 },
2073 GPSLongitude => {
2074 Require => 'QuickTime:GPSCoordinates',
2075 Groups => { 2 => 'Location' },
2076 ValueConv => 'my @c = split " ", $val; $c[1]',
2077 PrintConv => q{
2078 require Image::ExifTool::GPS;
2079 Image::ExifTool::GPS::ToDMS($self, $val, 1, 'E');
2080 },
2081 },
2082 # split altitude into GPSAltitude/GPSAltitudeRef like EXIF and XMP
2083 GPSAltitude => {
2084 Require => 'QuickTime:GPSCoordinates',
2085 Groups => { 2 => 'Location' },
2086 Priority => 0, # (because it may not exist)
2087 ValueConv => 'my @c = split " ", $val; defined $c[2] ? abs($c[2]) : undef',
2088 PrintConv => '"$val m"',
2089 },
2090 GPSAltitudeRef => {
2091 Require => 'QuickTime:GPSCoordinates',
2092 Groups => { 2 => 'Location' },
2093 Priority => 0, # (because altitude information may not exist)
2094 ValueConv => 'my @c = split " ", $val; defined $c[2] ? ($c[2] < 0 ? 1 : 0) : undef',
2095 PrintConv => {
2096 0 => 'Above Sea Level',
2097 1 => 'Below Sea Level',
2098 },
2099 },
2100);
2101
2102# add our composite tags
2103Image::ExifTool::AddCompositeTags('Image::ExifTool::QuickTime');
2104
2105
2106#------------------------------------------------------------------------------
2107# Calculate rotation of video track
2108# Inputs: 0) ExifTool object ref
2109# Returns: rotation angle or undef
2110sub CalcRotation($)
2111{
2112 my $exifTool = shift;
2113 my $value = $$exifTool{VALUE};
2114 my ($i, $track);
2115 # get the video track family 1 group (ie. "Track1");
2116 for ($i=0; ; ++$i) {
2117 my $idx = $i ? " ($i)" : '';
2118 my $tag = "HandlerType$idx";
2119 last unless $$value{$tag};
2120 next unless $$value{$tag} eq 'vide';
2121 $track = $exifTool->GetGroup($tag, 1);
2122 last;
2123 }
2124 return undef unless $track;
2125 # get the video track matrix
2126 for ($i=0; ; ++$i) {
2127 my $idx = $i ? " ($i)" : '';
2128 my $tag = "MatrixStructure$idx";
2129 last unless $$value{$tag};
2130 next unless $exifTool->GetGroup($tag, 1) eq $track;
2131 my @a = split ' ', $$value{$tag};
2132 return undef unless $a[0] or $a[1];
2133 # calculate the rotation angle (assume uniform rotation)
2134 my $angle = atan2($a[1], $a[0]) * 180 / 3.14159;
2135 $angle += 360 if $angle < 0;
2136 return int($angle * 1000 + 0.5) / 1000;
2137 }
2138 return undef;
2139}
2140
2141#------------------------------------------------------------------------------
2142# Determine the average sample rate from a time-to-sample table
2143# Inputs: 0) ExifTool object ref, 1) time-to-sample table data ref
2144# Returns: average sample rate (in Hz)
2145sub CalcSampleRate($$)
2146{
2147 my ($exifTool, $valPt) = @_;
2148 my @dat = unpack('N*', $$valPt);
2149 my ($num, $dur) = (0, 0);
2150 my $i;
2151 for ($i=2; $i<@dat-1; $i+=2) {
2152 $num += $dat[$i]; # total number of samples
2153 $dur += $dat[$i] * $dat[$i+1]; # total sample duration
2154 }
2155 return undef unless $num and $dur and $$exifTool{MediaTS};
2156 return $num * $$exifTool{MediaTS} / $dur;
2157}
2158
2159#------------------------------------------------------------------------------
2160# Fix incorrect format for ImageWidth/Height as written by Pentax
2161sub FixWrongFormat($)
2162{
2163 my $val = shift;
2164 return undef unless $val;
2165 if ($val & 0xffff0000) {
2166 $val = unpack('n',pack('N',$val));
2167 }
2168 return $val;
2169}
2170
2171#------------------------------------------------------------------------------
2172# Convert ISO 6709 string to standard lag/lon format
2173# Inputs: 0) ISO 6709 string (lat, lon, and optional alt)
2174# Returns: position in decimal degress with altitude if available
2175# Notes: Wikipedia indicates altitude may be in feet -- how is this specified?
2176sub ConvertISO6709($)
2177{
2178 my $val = shift;
2179 if ($val =~ /^([-+]\d{2}(?:\.\d*)?)([-+]\d{3}(?:\.\d*)?)([-+]\d+)?/) {
2180 $val = ($1 + 0) . ' ' . ($2 + 0);
2181 $val .= ' ' . ($3 + 0) if $3;
2182 } elsif ($val =~ /^([-+])(\d{2})(\d{2}(?:\.\d*)?)([-+])(\d{3})(\d{2}(?:\.\d*)?)([-+]\d+)?/) {
2183 my $lat = $2 + $3 / 60;
2184 $lat = -$lat if $1 eq '-';
2185 my $lon = $5 + $6 / 60;
2186 $lon = -$lon if $4 eq '-';
2187 $val = "$lat $lon";
2188 $val .= ' ' . ($7 + 0) if $7;
2189 } elsif ($val =~ /^([-+])(\d{2})(\d{2})(\d{2}(?:\.\d*)?)([-+])(\d{3})(\d{2})(\d{2}(?:\.\d*)?)([-+]\d+)?/) {
2190 my $lat = $2 + $3 / 60 + $4 / 3600;
2191 $lat = -$lat if $1 eq '-';
2192 my $lon = $6 + $7 / 60 + $8 / 3600;
2193 $lon = -$lon if $5 eq '-';
2194 $val = "$lat $lon";
2195 $val .= ' ' . ($9 + 0) if $9;
2196 }
2197 return $val;
2198}
2199
2200#------------------------------------------------------------------------------
2201# Format GPSCoordinates for printing
2202# Inputs: 0) string with numerical lat, lon and optional alt, separated by spaces
2203# 1) ExifTool object reference
2204# Returns: PrintConv value
2205sub PrintGPSCoordinates($)
2206{
2207 my ($val, $exifTool) = @_;
2208 require Image::ExifTool::GPS;
2209 my @v = split ' ', $val;
2210 my $prt = Image::ExifTool::GPS::ToDMS($exifTool, $v[0], 1, "N") . ', ' .
2211 Image::ExifTool::GPS::ToDMS($exifTool, $v[1], 1, "E");
2212 if (defined $v[2]) {
2213 $prt .= ', ' . ($v[2] < 0 ? -$v[2] . ' m Below' : $v[2] . ' m Above') . ' Sea Level';
2214 }
2215 return $prt;
2216}
2217
2218#------------------------------------------------------------------------------
2219# Unpack packed ISO 639/T language code
2220# Inputs: 0) packed language code (or undef)
2221# Returns: language code, or undef for default language, or 'err' for format error
2222sub UnpackLang($)
2223{
2224 my $lang = shift;
2225 if ($lang) {
2226 # language code is packed in 5-bit characters
2227 $lang = pack "C*", map { (($lang>>$_)&0x1f)+0x60 } 10, 5, 0;
2228 # validate language code
2229 if ($lang =~ /^[a-z]+$/) {
2230 # treat 'eng' or 'und' as the default language
2231 undef $lang if $lang eq 'und' or $lang eq 'eng';
2232 } else {
2233 $lang = 'err'; # invalid language code
2234 }
2235 }
2236 return $lang;
2237}
2238
2239#------------------------------------------------------------------------------
2240# Process MPEG-4 MTDT atom (ref 11)
2241# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
2242# Returns: 1 on success
2243sub ProcessMetaData($$$)
2244{
2245 my ($exifTool, $dirInfo, $tagTablePtr) = @_;
2246 my $dataPt = $$dirInfo{DataPt};
2247 my $dirLen = length $$dataPt;
2248 my $verbose = $exifTool->Options('Verbose');
2249 return 0 unless $dirLen >= 2;
2250 my $count = Get16u($dataPt, 0);
2251 $verbose and $exifTool->VerboseDir('MetaData', $count);
2252 my $i;
2253 my $pos = 2;
2254 for ($i=0; $i<$count; ++$i) {
2255 last if $pos + 10 > $dirLen;
2256 my $size = Get16u($dataPt, $pos);
2257 last if $size < 10 or $size + $pos > $dirLen;
2258 my $tag = Get32u($dataPt, $pos + 2);
2259 my $lang = Get16u($dataPt, $pos + 6);
2260 my $enc = Get16u($dataPt, $pos + 8);
2261 my $val = substr($$dataPt, $pos + 10, $size);
2262 my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
2263 if ($tagInfo) {
2264 # convert language code to ASCII (ignore read-only bit)
2265 $lang = UnpackLang($lang);
2266 # handle alternate languages
2267 if ($lang) {
2268 my $langInfo = Image::ExifTool::GetLangInfo($tagInfo, $lang);
2269 $tagInfo = $langInfo if $langInfo;
2270 }
2271 $verbose and $exifTool->VerboseInfo($tag, $tagInfo,
2272 Value => $val,
2273 DataPt => $dataPt,
2274 Start => $pos + 10,
2275 Size => $size - 10,
2276 );
2277 # convert from UTF-16 BE if necessary
2278 $val = $exifTool->Decode($val, 'UCS2') if $enc == 1;
2279 if ($enc == 0 and $$tagInfo{Unknown}) {
2280 # binary data
2281 $exifTool->FoundTag($tagInfo, \$val);
2282 } else {
2283 $exifTool->FoundTag($tagInfo, $val);
2284 }
2285 }
2286 $pos += $size;
2287 }
2288 return 1;
2289}
2290
2291#------------------------------------------------------------------------------
2292# Process Meta keys and add tags to the ItemList table ('mdta' handler) (ref PH)
2293# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
2294# Returns: 1 on success
2295sub ProcessKeys($$$)
2296{
2297 my ($exifTool, $dirInfo, $tagTablePtr) = @_;
2298 my $dataPt = $$dirInfo{DataPt};
2299 my $dirLen = length $$dataPt;
2300 my $out;
2301 if ($exifTool->Options('Verbose')) {
2302 $exifTool->VerboseDir('Keys');
2303 $out = $exifTool->Options('TextOut');
2304 }
2305 my $pos = 8;
2306 my $index = 1;
2307 my $infoTable = GetTagTable('Image::ExifTool::QuickTime::ItemList');
2308 my $userTable = GetTagTable('Image::ExifTool::QuickTime::UserData');
2309 while ($pos < $dirLen - 4) {
2310 my $len = unpack("x${pos}N", $$dataPt);
2311 last if $len < 8 or $pos + $len > $dirLen;
2312 delete $$tagTablePtr{$index};
2313 my $ns = substr($$dataPt, $pos + 4, 4);
2314 my $tag = substr($$dataPt, $pos + 8, $len - 8);
2315 $tag =~ s/\0.*//s; # truncate at null
2316 if ($ns eq 'mdta') {
2317 $tag =~ s/^com\.apple\.quicktime\.//; # remove common apple quicktime domain
2318 }
2319 next unless $tag;
2320 # (I have some samples where the tag is a reversed ItemList or UserData tag ID)
2321 my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
2322 unless ($tagInfo) {
2323 $tagInfo = $exifTool->GetTagInfo($infoTable, $tag);
2324 unless ($tagInfo) {
2325 $tagInfo = $exifTool->GetTagInfo($userTable, $tag);
2326 if (not $tagInfo and $tag =~ /^\w{3}\xa9$/) {
2327 $tag = pack('N', unpack('V', $tag));
2328 $tagInfo = $exifTool->GetTagInfo($infoTable, $tag);
2329 $tagInfo or $tagInfo = $exifTool->GetTagInfo($userTable, $tag);
2330 }
2331 }
2332 }
2333 my $newInfo;
2334 if ($tagInfo) {
2335 $newInfo = {
2336 Name => $$tagInfo{Name},
2337 Format => $$tagInfo{Format},
2338 ValueConv => $$tagInfo{ValueConv},
2339 PrintConv => $$tagInfo{PrintConv},
2340 };
2341 my $groups = $$tagInfo{Groups};
2342 $$newInfo{Groups} = { %$groups } if $groups;
2343 } elsif ($tag =~ /^[-\w.]+$/) {
2344 # create info for tags with reasonable id's
2345 my $name = $tag;
2346 $name =~ s/\.(.)/\U$1/g;
2347 $newInfo = { Name => ucfirst($name) };
2348 }
2349 # substitute this tag in the ItemList table with the given index
2350 delete $$infoTable{$index};
2351 if ($newInfo) {
2352 Image::ExifTool::AddTagToTable($infoTable, $index, $newInfo);
2353 $out and printf $out "%sAdded ItemList Tag 0x%.4x = $tag\n", $exifTool->{INDENT}, $index;
2354 }
2355 $pos += $len;
2356 ++$index;
2357 }
2358 return 1;
2359}
2360
2361#------------------------------------------------------------------------------
2362# Process a QuickTime atom
2363# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) optional tag table ref
2364# Returns: 1 on success
2365sub ProcessMOV($$;$)
2366{
2367 my ($exifTool, $dirInfo, $tagTablePtr) = @_;
2368 my $raf = $$dirInfo{RAF};
2369 my $dataPt = $$dirInfo{DataPt};
2370 my $verbose = $exifTool->Options('Verbose');
2371 my $dataPos = $$dirInfo{Base} || 0;
2372 my ($buff, $tag, $size, $track);
2373
2374 # more convenient to package data as a RandomAccess file
2375 $raf or $raf = new File::RandomAccess($dataPt);
2376 # skip leading bytes if necessary
2377 if ($$dirInfo{DirStart}) {
2378 $raf->Seek($$dirInfo{DirStart}, 1) or return 0;
2379 $dataPos += $$dirInfo{DirStart};
2380 }
2381 # read size/tag name atom header
2382 $raf->Read($buff,8) == 8 or return 0;
2383 $dataPos += 8;
2384 $tagTablePtr or $tagTablePtr = GetTagTable('Image::ExifTool::QuickTime::Main');
2385 ($size, $tag) = unpack('Na4', $buff);
2386 if ($dataPt) {
2387 $verbose and $exifTool->VerboseDir($$dirInfo{DirName});
2388 } else {
2389 # check on file type if called with a RAF
2390 $$tagTablePtr{$tag} or return 0;
2391 if ($tag eq 'ftyp') {
2392 # read ahead 4 bytes to see what type of file this is
2393 my $fileType;
2394 if ($raf->Read($buff, 4) == 4) {
2395 $raf->Seek(-4, 1);
2396 # see if we know the extension for this file type
2397 $fileType = $1 if $ftypLookup{$buff} and $ftypLookup{$buff} =~ /\(\.(\w+)/;
2398 }
2399 $fileType or $fileType = 'MP4'; # default to MP4
2400 $exifTool->SetFileType($fileType, $mimeLookup{$fileType} || 'video/mp4');
2401 } else {
2402 $exifTool->SetFileType(); # MOV
2403 }
2404 SetByteOrder('MM');
2405 }
2406 for (;;) {
2407 if ($size < 8) {
2408 last if $size == 0;
2409 $size == 1 or $exifTool->Warn('Invalid atom size'), last;
2410 $raf->Read($buff, 8) == 8 or last;
2411 $dataPos += 8;
2412 my ($hi, $lo) = unpack('NN', $buff);
2413 $size = $lo;
2414 if ($hi or $lo > 0x7fffffff) {
2415 if ($hi > 0x7fffffff) {
2416 $exifTool->Warn('Invalid atom size');
2417 last;
2418 } elsif (not $exifTool->Options('LargeFileSupport')) {
2419 $exifTool->Warn('End of processing at large atom (LargeFileSupport not enabled)');
2420 last;
2421 }
2422 }
2423 $size = $hi * 4294967296 + $lo;
2424 }
2425 $size -= 8;
2426 my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
2427 # allow numerical tag ID's
2428 unless ($tagInfo) {
2429 my $num = unpack('N', $tag);
2430 if ($$tagTablePtr{$num}) {
2431 $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $num);
2432 $tag = $num;
2433 }
2434 }
2435 # generate tagInfo if Unknown option set
2436 if (not defined $tagInfo and ($exifTool->{OPTIONS}->{Unknown} or
2437 $verbose or $tag =~ /^\xa9/))
2438 {
2439 my $name = $tag;
2440 my $n = ($name =~ s/([\x00-\x1f\x7f-\xff])/'x'.unpack('H*',$1)/eg);
2441 # print in hex if tag is numerical
2442 $name = sprintf('0x%.4x',unpack('N',$tag)) if $n > 2;
2443 if ($name =~ /^xa9(.*)/) {
2444 $tagInfo = {
2445 Name => "UserData_$1",
2446 Description => "User Data $1",
2447 };
2448 } else {
2449 $tagInfo = {
2450 Name => "Unknown_$name",
2451 Description => "Unknown $name",
2452 Unknown => 1,
2453 Binary => 1,
2454 };
2455 }
2456 Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo);
2457 }
2458 # save required tag sizes
2459 $exifTool->HandleTag($tagTablePtr, "$tag-size", $size) if $$tagTablePtr{"$tag-size"};
2460 # load values only if associated with a tag (or verbose) and < 16MB long
2461 if ((defined $tagInfo or $verbose) and $size < 0x1000000) {
2462 my $val;
2463 unless ($raf->Read($val, $size) == $size) {
2464 $exifTool->Warn("Truncated '$tag' data");
2465 last;
2466 }
2467 # use value to get tag info if necessary
2468 $tagInfo or $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag, \$val);
2469 my $hasData = ($$dirInfo{HasData} and $val =~ /\0...data\0/s);
2470 if ($verbose and not $hasData) {
2471 $exifTool->VerboseInfo($tag, $tagInfo,
2472 Value => $val,
2473 DataPt => \$val,
2474 DataPos => $dataPos,
2475 );
2476 }
2477 if ($tagInfo) {
2478 my $subdir = $$tagInfo{SubDirectory};
2479 if ($subdir) {
2480 my $start = $$subdir{Start} || 0;
2481 my ($base, $dPos) = ($dataPos, 0);
2482 if ($$subdir{Base}) {
2483 $dPos -= eval $$subdir{Base};
2484 $base -= $dPos;
2485 }
2486 my %dirInfo = (
2487 DataPt => \$val,
2488 DataLen => $size,
2489 DirStart => $start,
2490 DirLen => $size - $start,
2491 DirName => $$subdir{DirName} || $$tagInfo{Name},
2492 HasData => $$subdir{HasData},
2493 Multi => $$subdir{Multi},
2494 DataPos => $dPos,
2495 # Base needed for IsOffset tags in binary data
2496 Base => $base,
2497 );
2498 if ($$subdir{ByteOrder} and $$subdir{ByteOrder} =~ /^Little/) {
2499 SetByteOrder('II');
2500 }
2501 my $oldGroup1 = $exifTool->{SET_GROUP1};
2502 if ($$tagInfo{Name} eq 'Track') {
2503 $track or $track = 0;
2504 $exifTool->{SET_GROUP1} = 'Track' . (++$track);
2505 }
2506 my $subTable = GetTagTable($$subdir{TagTable});
2507 my $proc = $$subdir{ProcessProc};
2508 $exifTool->ProcessDirectory(\%dirInfo, $subTable, $proc) if $size > $start;
2509 $exifTool->{SET_GROUP1} = $oldGroup1;
2510 SetByteOrder('MM');
2511 } elsif ($hasData) {
2512 # handle atoms containing 'data' tags
2513 # (currently ignore contained atoms: 'itif', 'name', etc.)
2514 my $pos = 0;
2515 for (;;) {
2516 last if $pos + 16 > $size;
2517 my ($len, $type, $flags, $ctry, $lang) = unpack("x${pos}Na4Nnn", $val);
2518 last if $pos + $len > $size;
2519 my $value;
2520 my $format = $$tagInfo{Format};
2521 if ($type eq 'data' and $len >= 16) {
2522 $pos += 16;
2523 $len -= 16;
2524 $value = substr($val, $pos, $len);
2525 # format flags (ref 12):
2526 # 0x0=binary, 0x1=UTF-8, 0x2=UTF-16, 0x3=ShiftJIS,
2527 # 0x4=UTF-8 0x5=UTF-16, 0xd=JPEG, 0xe=PNG,
2528 # 0x15=signed int, 0x16=unsigned int, 0x17=float,
2529 # 0x18=double, 0x1b=BMP, 0x1c='meta' atom
2530 if ($stringEncoding{$flags}) {
2531 # handle all string formats
2532 $value = $exifTool->Decode($value, $stringEncoding{$flags});
2533 } else {
2534 if (not $format) {
2535 if ($flags == 0x15 or $flags == 0x16) {
2536 $format = { 1=>'int8', 2=>'int16', 4=>'int32' }->{$len};
2537 $format .= $flags == 0x15 ? 's' : 'u' if $format;
2538 } elsif ($flags == 0x17) {
2539 $format = 'float';
2540 } elsif ($flags == 0x18) {
2541 $format = 'double';
2542 } elsif ($flags == 0x00) {
2543 # read 1 and 2-byte binary as integers
2544 if ($len == 1) {
2545 $format = 'int8u',
2546 } elsif ($len == 2) {
2547 $format = 'int16u',
2548 }
2549 }
2550 }
2551 if ($format) {
2552 $value = ReadValue(\$value, 0, $format, $$tagInfo{Count}, $len);
2553 } elsif (not $$tagInfo{ValueConv}) {
2554 # make binary data a scalar reference unless a ValueConv exists
2555 my $buf = $value;
2556 $value = \$buf;
2557 }
2558 }
2559 }
2560 my $langInfo;
2561 if ($ctry or $lang) {
2562 # ignore country ('ctry') and language lists ('lang') for now
2563 undef $ctry if $ctry and $ctry <= 255;
2564 undef $lang if $lang and $lang <= 255;
2565 $lang = UnpackLang($lang);
2566 # add country code if specified
2567 if ($ctry) {
2568 $ctry = unpack('a2',pack('n',$ctry)); # unpack as ISO 3166-1
2569 # treat 'ZZ' like a default country (see ref 12)
2570 undef $ctry if $ctry eq 'ZZ';
2571 if ($ctry and $ctry =~ /^[A-Z]{2}$/) {
2572 $lang or $lang = 'und';
2573 $lang .= "-$ctry";
2574 }
2575 }
2576 $langInfo = Image::ExifTool::GetLangInfo($tagInfo, $lang) if $lang;
2577 }
2578 $langInfo or $langInfo = $tagInfo;
2579 $exifTool->VerboseInfo($tag, $langInfo,
2580 Value => ref $value ? $$value : $value,
2581 DataPt => \$val,
2582 DataPos => $dataPos,
2583 Start => $pos,
2584 Size => $len,
2585 Format => $format,
2586 Extra => sprintf(", Type='$type', Flags=0x%x",$flags)
2587 ) if $verbose;
2588 $exifTool->FoundTag($langInfo, $value) if defined $value;
2589 $pos += $len;
2590 }
2591 } elsif ($tag =~ /^\xa9/) {
2592 # parse international text to extract all languages
2593 my $pos = 0;
2594 for (;;) {
2595 last if $pos + 4 > $size;
2596 my ($len, $lang) = unpack("x${pos}nn", $val);
2597 $pos += 4;
2598 # according to the QuickTime spec (ref 12), $len should include
2599 # 4 bytes for length and type words, but nobody (including
2600 # Apple, Pentax and Kodak) seems to add these in, so try
2601 # to allow for either
2602 if ($pos + $len > $size) {
2603 $len -= 4;
2604 last if $pos + $len > $size or $len < 0;
2605 }
2606 # ignore any empty entries (or null padding) after the first
2607 next if not $len and $pos;
2608 my $str = substr($val, $pos, $len);
2609 my $langInfo;
2610 if ($lang < 0x400) {
2611 # this is a Macintosh language code
2612 # a language code of 0 is Macintosh english, so treat as default
2613 if ($lang) {
2614 # use Font.pm to look up language string
2615 require Image::ExifTool::Font;
2616 $lang = $Image::ExifTool::Font::ttLang{Macintosh}{$lang};
2617 }
2618 # the spec says only "Macintosh text encoding", so
2619 # I can only assume that it is the most common one
2620 $str = $exifTool->Decode($str, 'MacRoman');
2621 } else {
2622 # convert language code to ASCII (ignore read-only bit)
2623 $lang = UnpackLang($lang);
2624 # may be either UTF-8 or UTF-16BE
2625 my $enc = $str=~s/^\xfe\xff// ? 'UTF16' : 'UTF8';
2626 $str = $exifTool->Decode($str, $enc);
2627 }
2628 $langInfo = Image::ExifTool::GetLangInfo($tagInfo, $lang) if $lang;
2629 $exifTool->FoundTag($langInfo || $tagInfo, $str);
2630 $pos += $len;
2631 }
2632 } else {
2633 if ($$tagInfo{Format}) {
2634 $val = ReadValue(\$val, 0, $$tagInfo{Format}, $$tagInfo{Count}, length($val));
2635 }
2636 $exifTool->FoundTag($tagInfo, $val);
2637 }
2638 }
2639 } else {
2640 $raf->Seek($size, 1) or $exifTool->Warn("Truncated '$tag' data"), last;
2641 }
2642 $raf->Read($buff, 8) == 8 or last;
2643 $dataPos += $size + 8;
2644 ($size, $tag) = unpack('Na4', $buff);
2645 }
2646 return 1;
2647}
2648
2649#------------------------------------------------------------------------------
2650# Process a QuickTime Image File
2651# Inputs: 0) ExifTool object reference, 1) directory information reference
2652# Returns: 1 on success
2653sub ProcessQTIF($$)
2654{
2655 my ($exifTool, $dirInfo) = @_;
2656 my $table = GetTagTable('Image::ExifTool::QuickTime::ImageFile');
2657 return ProcessMOV($exifTool, $dirInfo, $table);
2658}
2659
26601; # end
2661
2662__END__
2663
2664=head1 NAME
2665
2666Image::ExifTool::QuickTime - Read QuickTime and MP4 meta information
2667
2668=head1 SYNOPSIS
2669
2670This module is used by Image::ExifTool
2671
2672=head1 DESCRIPTION
2673
2674This module contains routines required by Image::ExifTool to extract
2675information from QuickTime and MP4 video, and M4A audio files.
2676
2677=head1 AUTHOR
2678
2679Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca)
2680
2681This library is free software; you can redistribute it and/or modify it
2682under the same terms as Perl itself.
2683
2684=head1 REFERENCES
2685
2686=over 4
2687
2688=item L<http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html>
2689
2690=item L<http://search.cpan.org/dist/MP4-Info-1.04/>
2691
2692=item L<http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt>
2693
2694=item L<http://wiki.multimedia.cx/index.php?title=Apple_QuickTime>
2695
2696=item L<http://atomicparsley.sourceforge.net/mpeg-4files.html>
2697
2698=item L<http://wiki.multimedia.cx/index.php?title=QuickTime_container>
2699
2700=item L<http://code.google.com/p/mp4v2/wiki/iTunesMetadata>
2701
2702=item L<http://www.canieti.com.mx/assets/files/1011/IEC_100_1384_DC.pdf>
2703
2704=item L<http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf>
2705
2706=back
2707
2708=head1 SEE ALSO
2709
2710L<Image::ExifTool::TagNames/QuickTime Tags>,
2711L<Image::ExifTool(3pm)|Image::ExifTool>
2712
2713=cut
2714
Note: See TracBrowser for help on using the repository browser.