<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">#------------------------------------------------------------------------------
# File:         QuickTime.pm
#
# Description:  Read QuickTime, MP4 and M4A meta information
#
# Revisions:    10/04/2005 - P. Harvey Created
#               12/19/2005 - P. Harvey Added MP4 support
#               09/22/2006 - P. Harvey Added M4A support
#               07/27/2010 - P. Harvey Updated to 2010-05-03 QuickTime spec
#
# References:   1) http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html
#               2) http://search.cpan.org/dist/MP4-Info-1.04/
#               3) http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
#               4) http://wiki.multimedia.cx/index.php?title=Apple_QuickTime
#               5) ISO 14496-12 (http://neuron2.net/library/avc/c041828_ISO_IEC_14496-12_2005(E).pdf)
#               6) ISO 14496-16 (http://www.iec-normen.de/previewpdf/info_isoiec14496-16%7Bed2.0%7Den.pdf)
#               7) http://atomicparsley.sourceforge.net/mpeg-4files.html
#               8) http://wiki.multimedia.cx/index.php?title=QuickTime_container
#               9) http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart3.pdf (Oct 2008)
#               10) http://code.google.com/p/mp4v2/wiki/iTunesMetadata
#               11) http://www.canieti.com.mx/assets/files/1011/IEC_100_1384_DC.pdf
#               12) QuickTime file format specification 2010-05-03
#               13) http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf
#               14) http://standards.iso.org/ittf/PubliclyAvailableStandards/c051533_ISO_IEC_14496-12_2008.zip
#------------------------------------------------------------------------------

package Image::ExifTool::QuickTime;

use strict;
use vars qw($VERSION);
use Image::ExifTool qw(:DataAccess :Utils);
use Image::ExifTool::Exif;

$VERSION = '1.48';

sub FixWrongFormat($);
sub ProcessMOV($$;$);
sub ProcessKeys($$$);
sub ProcessMetaData($$$);
sub ConvertISO6709($);
sub PrintGPSCoordinates($);

# information for time/date-based tags (time zero is Jan 1, 1904)
my %timeInfo = (
    # Note: This value will be in UTC if generated by a system that is aware of the time zone
    ValueConv =&gt; 'ConvertUnixTime($val - ((66 * 365 + 17) * 24 * 3600))',
    PrintConv =&gt; '$self-&gt;ConvertDateTime($val)',
    # (can't put Groups here because they aren't constant!)
);
# information for duration tags
my %durationInfo = (
    ValueConv =&gt; '$$self{TimeScale} ? $val / $$self{TimeScale} : $val',
    PrintConv =&gt; '$$self{TimeScale} ? ConvertDuration($val) : $val',
);

# 4-character Vendor ID codes (ref PH)
my %vendorID = (
    appl =&gt; 'Apple',
    fe20 =&gt; 'Olympus (fe20)', # (FE200)
    FFMP =&gt; 'FFmpeg',
   'GIC '=&gt; 'General Imaging Co.',
    kdak =&gt; 'Kodak',
    KMPI =&gt; 'Konica-Minolta',
    mino =&gt; 'Minolta',
    niko =&gt; 'Nikon',
    NIKO =&gt; 'Nikon',
    olym =&gt; 'Olympus',
    pana =&gt; 'Panasonic',
    pent =&gt; 'Pentax',
    pr01 =&gt; 'Olympus (pr01)', # (FE100,FE110,FE115)
    sany =&gt; 'Sanyo',
   'SMI '=&gt; 'Sorenson Media Inc.',
    ZORA =&gt; 'Zoran Corporation',
);

# QuickTime data atom encodings for string types (ref 12)
my %stringEncoding = (
    1 =&gt; 'UTF8',
    2 =&gt; 'UTF16',
    3 =&gt; 'ShiftJIS',
    4 =&gt; 'UTF8',
    5 =&gt; 'UTF16',
);

# MIME types for all entries in the ftypLookup with file extensions
# (defaults to 'video/mp4' if not found in this lookup)
my %mimeLookup = (
   '3G2' =&gt; 'video/3gpp2',
   '3GP' =&gt; 'video/3gpp',
    DVB  =&gt; 'video/vnd.dvb.file',
    F4A  =&gt; 'audio/mp4',
    F4B  =&gt; 'audio/mp4',
    JP2  =&gt; 'image/jp2',
    JPM  =&gt; 'image/jpm',
    JPX  =&gt; 'image/jpx',
    M4A  =&gt; 'audio/mp4',
    M4B  =&gt; 'audio/mp4',
    M4P  =&gt; 'audio/mp4',
    M4V  =&gt; 'video/x-m4v',
    MOV  =&gt; 'video/quicktime',
    MQV  =&gt; 'video/quicktime',
);

my %graphicsMode = (
    # (ref http://homepage.mac.com/vanhoek/MovieGuts%20docs/64.html)
    0x00 =&gt; 'srcCopy',
    0x01 =&gt; 'srcOr',
    0x02 =&gt; 'srcXor',
    0x03 =&gt; 'srcBic',
    0x04 =&gt; 'notSrcCopy',
    0x05 =&gt; 'notSrcOr',
    0x06 =&gt; 'notSrcXor',
    0x07 =&gt; 'notSrcBic',
    0x08 =&gt; 'patCopy',
    0x09 =&gt; 'patOr',
    0x0a =&gt; 'patXor',
    0x0b =&gt; 'patBic',
    0x0c =&gt; 'notPatCopy',
    0x0d =&gt; 'notPatOr',
    0x0e =&gt; 'notPatXor',
    0x0f =&gt; 'notPatBic',
    0x20 =&gt; 'blend',
    0x21 =&gt; 'addPin',
    0x22 =&gt; 'addOver',
    0x23 =&gt; 'subPin',
    0x24 =&gt; 'transparent',
    0x25 =&gt; 'addMax',
    0x26 =&gt; 'subOver',
    0x27 =&gt; 'addMin',
    0x31 =&gt; 'grayishTextOr',
    0x32 =&gt; 'hilite',
    0x40 =&gt; 'ditherCopy',
    # the following ref ISO/IEC 15444-3
    0x100 =&gt; 'Alpha',
    0x101 =&gt; 'White Alpha',
    0x102 =&gt; 'Pre-multiplied Black Alpha',
    0x110 =&gt; 'Component Alpha',
);

# look up file type from ftyp atom type, with MIME type in comment if known
# (ref http://www.ftyps.com/)
my %ftypLookup = (
    '3g2a' =&gt; '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-0 V1.0', # video/3gpp2
    '3g2b' =&gt; '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-A V1.0.0', # video/3gpp2
    '3g2c' =&gt; '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-B v1.0', # video/3gpp2
    '3ge6' =&gt; '3GPP (.3GP) Release 6 MBMS Extended Presentations', # video/3gpp
    '3ge7' =&gt; '3GPP (.3GP) Release 7 MBMS Extended Presentations', # video/3gpp
    '3gg6' =&gt; '3GPP Release 6 General Profile', # video/3gpp
    '3gp1' =&gt; '3GPP Media (.3GP) Release 1 (probably non-existent)', # video/3gpp
    '3gp2' =&gt; '3GPP Media (.3GP) Release 2 (probably non-existent)', # video/3gpp
    '3gp3' =&gt; '3GPP Media (.3GP) Release 3 (probably non-existent)', # video/3gpp
    '3gp4' =&gt; '3GPP Media (.3GP) Release 4', # video/3gpp
    '3gp5' =&gt; '3GPP Media (.3GP) Release 5', # video/3gpp
    '3gp6' =&gt; '3GPP Media (.3GP) Release 6 Basic Profile', # video/3gpp
    '3gp6' =&gt; '3GPP Media (.3GP) Release 6 Progressive Download', # video/3gpp
    '3gp6' =&gt; '3GPP Media (.3GP) Release 6 Streaming Servers', # video/3gpp
    '3gs7' =&gt; '3GPP Media (.3GP) Release 7 Streaming Servers', # video/3gpp
    'avc1' =&gt; 'MP4 Base w/ AVC ext [ISO 14496-12:2005]', # video/mp4
    'CAEP' =&gt; 'Canon Digital Camera',
    'caqv' =&gt; 'Casio Digital Camera',
    'CDes' =&gt; 'Convergent Design',
    'da0a' =&gt; 'DMB MAF w/ MPEG Layer II aud, MOT slides, DLS, JPG/PNG/MNG images',
    'da0b' =&gt; 'DMB MAF, extending DA0A, with 3GPP timed text, DID, TVA, REL, IPMP',
    'da1a' =&gt; 'DMB MAF audio with ER-BSAC audio, JPG/PNG/MNG images',
    'da1b' =&gt; 'DMB MAF, extending da1a, with 3GPP timed text, DID, TVA, REL, IPMP',
    'da2a' =&gt; 'DMB MAF aud w/ HE-AAC v2 aud, MOT slides, DLS, JPG/PNG/MNG images',
    'da2b' =&gt; 'DMB MAF, extending da2a, with 3GPP timed text, DID, TVA, REL, IPMP',
    'da3a' =&gt; 'DMB MAF aud with HE-AAC aud, JPG/PNG/MNG images',
    'da3b' =&gt; 'DMB MAF, extending da3a w/ BIFS, 3GPP timed text, DID, TVA, REL, IPMP',
    'dmb1' =&gt; 'DMB MAF supporting all the components defined in the specification',
    'dmpf' =&gt; 'Digital Media Project', # various
    'drc1' =&gt; 'Dirac (wavelet compression), encapsulated in ISO base media (MP4)',
    'dv1a' =&gt; 'DMB MAF vid w/ AVC vid, ER-BSAC aud, BIFS, JPG/PNG/MNG images, TS',
    'dv1b' =&gt; 'DMB MAF, extending dv1a, with 3GPP timed text, DID, TVA, REL, IPMP',
    'dv2a' =&gt; 'DMB MAF vid w/ AVC vid, HE-AAC v2 aud, BIFS, JPG/PNG/MNG images, TS',
    'dv2b' =&gt; 'DMB MAF, extending dv2a, with 3GPP timed text, DID, TVA, REL, IPMP',
    'dv3a' =&gt; 'DMB MAF vid w/ AVC vid, HE-AAC aud, BIFS, JPG/PNG/MNG images, TS',
    'dv3b' =&gt; 'DMB MAF, extending dv3a, with 3GPP timed text, DID, TVA, REL, IPMP',
    'dvr1' =&gt; 'DVB (.DVB) over RTP', # video/vnd.dvb.file
    'dvt1' =&gt; 'DVB (.DVB) over MPEG-2 Transport Stream', # video/vnd.dvb.file
    'F4A ' =&gt; 'Audio for Adobe Flash Player 9+ (.F4A)', # audio/mp4
    'F4B ' =&gt; 'Audio Book for Adobe Flash Player 9+ (.F4B)', # audio/mp4
    'F4P ' =&gt; 'Protected Video for Adobe Flash Player 9+ (.F4P)', # video/mp4
    'F4V ' =&gt; 'Video for Adobe Flash Player 9+ (.F4V)', # video/mp4
    'isc2' =&gt; 'ISMACryp 2.0 Encrypted File', # ?/enc-isoff-generic
    'iso2' =&gt; 'MP4 Base Media v2 [ISO 14496-12:2005]', # video/mp4
    'isom' =&gt; 'MP4  Base Media v1 [IS0 14496-12:2003]', # video/mp4
    'JP2 ' =&gt; 'JPEG 2000 Image (.JP2) [ISO 15444-1 ?]', # image/jp2
    'JP20' =&gt; 'Unknown, from GPAC samples (prob non-existent)',
    'jpm ' =&gt; 'JPEG 2000 Compound Image (.JPM) [ISO 15444-6]', # image/jpm
    'jpx ' =&gt; 'JPEG 2000 with extensions (.JPX) [ISO 15444-2]', # image/jpx
    'KDDI' =&gt; '3GPP2 EZmovie for KDDI 3G cellphones', # video/3gpp2
    'M4A ' =&gt; 'Apple iTunes AAC-LC (.M4A) Audio', # audio/x-m4a
    'M4B ' =&gt; 'Apple iTunes AAC-LC (.M4B) Audio Book', # audio/mp4
    'M4P ' =&gt; 'Apple iTunes AAC-LC (.M4P) AES Protected Audio', # audio/mp4
    'M4V ' =&gt; 'Apple iTunes Video (.M4V) Video', # video/x-m4v
    'M4VH' =&gt; 'Apple TV (.M4V)', # video/x-m4v
    'M4VP' =&gt; 'Apple iPhone (.M4V)', # video/x-m4v
    'mj2s' =&gt; 'Motion JPEG 2000 [ISO 15444-3] Simple Profile', # video/mj2
    'mjp2' =&gt; 'Motion JPEG 2000 [ISO 15444-3] General Profile', # video/mj2
    'mmp4' =&gt; 'MPEG-4/3GPP Mobile Profile (.MP4/3GP) (for NTT)', # video/mp4
    'mp21' =&gt; 'MPEG-21 [ISO/IEC 21000-9]', # various
    'mp41' =&gt; 'MP4 v1 [ISO 14496-1:ch13]', # video/mp4
    'mp42' =&gt; 'MP4 v2 [ISO 14496-14]', # video/mp4
    'mp71' =&gt; 'MP4 w/ MPEG-7 Metadata [per ISO 14496-12]', # various
    'MPPI' =&gt; 'Photo Player, MAF [ISO/IEC 23000-3]', # various
    'mqt ' =&gt; 'Sony / Mobile QuickTime (.MQV) US Patent 7,477,830 (Sony Corp)', # video/quicktime
    'MSNV' =&gt; 'MPEG-4 (.MP4) for SonyPSP', # audio/mp4
    'NDAS' =&gt; 'MP4 v2 [ISO 14496-14] Nero Digital AAC Audio', # audio/mp4
    'NDSC' =&gt; 'MPEG-4 (.MP4) Nero Cinema Profile', # video/mp4
    'NDSH' =&gt; 'MPEG-4 (.MP4) Nero HDTV Profile', # video/mp4
    'NDSM' =&gt; 'MPEG-4 (.MP4) Nero Mobile Profile', # video/mp4
    'NDSP' =&gt; 'MPEG-4 (.MP4) Nero Portable Profile', # video/mp4
    'NDSS' =&gt; 'MPEG-4 (.MP4) Nero Standard Profile', # video/mp4
    'NDXC' =&gt; 'H.264/MPEG-4 AVC (.MP4) Nero Cinema Profile', # video/mp4
    'NDXH' =&gt; 'H.264/MPEG-4 AVC (.MP4) Nero HDTV Profile', # video/mp4
    'NDXM' =&gt; 'H.264/MPEG-4 AVC (.MP4) Nero Mobile Profile', # video/mp4
    'NDXP' =&gt; 'H.264/MPEG-4 AVC (.MP4) Nero Portable Profile', # video/mp4
    'NDXS' =&gt; 'H.264/MPEG-4 AVC (.MP4) Nero Standard Profile', # video/mp4
    'odcf' =&gt; 'OMA DCF DRM Format 2.0 (OMA-TS-DRM-DCF-V2_0-20060303-A)', # various
    'opf2' =&gt; 'OMA PDCF DRM Format 2.1 (OMA-TS-DRM-DCF-V2_1-20070724-C)',
    'opx2' =&gt; 'OMA PDCF DRM + XBS extensions (OMA-TS-DRM_XBS-V1_0-20070529-C)',
    'pana' =&gt; 'Panasonic Digital Camera',
    'qt  ' =&gt; 'Apple QuickTime (.MOV/QT)', # video/quicktime
    'ROSS' =&gt; 'Ross Video',
    'sdv ' =&gt; 'SD Memory Card Video', # various?
    'ssc1' =&gt; 'Samsung stereoscopic, single stream',
    'ssc2' =&gt; 'Samsung stereoscopic, dual stream',
);

# QuickTime atoms
%Image::ExifTool::QuickTime::Main = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    NOTES =&gt; q{
        The QuickTime format is used for many different types of audio, video and
        image files.  Exiftool extracts meta information from the UserData atom
        (including some proprietary manufacturer-specific information), as well as
        extracting various audio, video and image parameters.  Tags with a question
        mark after their name are not extracted unless the Unknown option is set.
    },
    free =&gt; { Unknown =&gt; 1, Binary =&gt; 1 },
    skip =&gt; { Unknown =&gt; 1, Binary =&gt; 1 },
    wide =&gt; { Unknown =&gt; 1, Binary =&gt; 1 },
    ftyp =&gt; { #MP4
        Name =&gt; 'FileType',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::FileType' },
    },
    pnot =&gt; {
        Name =&gt; 'Preview',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::Preview' },
    },
    PICT =&gt; {
        Name =&gt; 'PreviewPICT',
        Binary =&gt; 1,
    },
    pict =&gt; { #8
        Name =&gt; 'PreviewPICT',
        Binary =&gt; 1,
    },
    moov =&gt; {
        Name =&gt; 'Movie',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::Movie' },
    },
    mdat =&gt; { Name =&gt; 'MovieData', Unknown =&gt; 1, Binary =&gt; 1 },
    'mdat-size' =&gt; {
        Name =&gt; 'MovieDataSize',
        Notes =&gt; q{
            not a real tag ID, this tag represents the size of the 'mdat' data in bytes
            and is used in the AvgBitrate calculation
        },
    },
    junk =&gt; { Unknown =&gt; 1, Binary =&gt; 1 }, #8
    uuid =&gt; [
        { #9 (MP4 files)
            Name =&gt; 'UUID-XMP',
            Condition =&gt; '$$valPt=~/^\xbe\x7a\xcf\xcb\x97\xa9\x42\xe8\x9c\x71\x99\x94\x91\xe3\xaf\xac/',
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::XMP::Main',
                Start =&gt; 16,
            },
        },
        { #11 (MP4 files)
            Name =&gt; 'UUID-PROF',
            Condition =&gt; '$$valPt=~/^PROF!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::QuickTime::Profile',
                Start =&gt; 24, # uid(16) + version(1) + flags(3) + count(4)
            },
        },
        { #PH (Flip MP4 files)
            Name =&gt; 'UUID-Flip',
            Condition =&gt; '$$valPt=~/\x4a\xb0\x3b\x0f\x61\x8d\x40\x75\x82\xb2\xd9\xfa\xce\xd3\x5f\xf5/',
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::QuickTime::Flip',
                Start =&gt; 16,
            },
        },
        # "\x98\x7f\xa3\xdf\x2a\x85\x43\xc0\x8f\x8f\xd9\x7c\x47\x1e\x8e\xea" - unknown data in Flip videos
        { #8
            Name =&gt; 'UUID-Unknown',
            Unknown =&gt; 1,
            Binary =&gt; 1,
        },
    ],
);

# MPEG-4 'ftyp' atom
# (ref http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html)
%Image::ExifTool::QuickTime::FileType = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    FORMAT =&gt; 'int32u',
    0 =&gt; {
        Name =&gt; 'MajorBrand',
        Format =&gt; 'undef[4]',
        PrintConv =&gt; \%ftypLookup,
    },
    1 =&gt; {
        Name =&gt; 'MinorVersion',
        Format =&gt; 'undef[4]',
        ValueConv =&gt; 'sprintf("%x.%x.%x", unpack("nCC", $val))',
    },
    2 =&gt; {
        Name =&gt; 'CompatibleBrands',
        Format =&gt; 'undef[$size-8]',
        # ignore any entry with a null, and return others as a list
        ValueConv =&gt; 'my @a=($val=~/.{4}/sg); @a=grep(!/\0/,@a); \@a', 
    },
);

# atoms used in QTIF files
%Image::ExifTool::QuickTime::ImageFile = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    GROUPS =&gt; { 2 =&gt; 'Image' },
    NOTES =&gt; 'Tags used in QTIF QuickTime Image Files.',
    idsc =&gt; {
        Name =&gt; 'ImageDescription',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::ImageDesc' },
    },
    idat =&gt; {
        Name =&gt; 'ImageData',
        Binary =&gt; 1,
    },
    iicc =&gt; {
        Name =&gt; 'ICC_Profile',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::ICC_Profile::Main' },
    },
);

# image description data block
%Image::ExifTool::QuickTime::ImageDesc = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    GROUPS =&gt; { 2 =&gt; 'Image' },
    FORMAT =&gt; 'int16u',
    2 =&gt; {
        Name =&gt; 'CompressorID',
        Format =&gt; 'string[4]',
# not very useful since this isn't a complete list and name is given below
#        # ref http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html
#        PrintConv =&gt; {
#            cvid =&gt; 'Cinepak',
#            jpeg =&gt; 'JPEG',
#           'smc '=&gt; 'Graphics',
#           'rle '=&gt; 'Animation',
#            rpza =&gt; 'Apple Video',
#            kpcd =&gt; 'Kodak Photo CD',
#           'png '=&gt; 'Portable Network Graphics',
#            mjpa =&gt; 'Motion-JPEG (format A)',
#            mjpb =&gt; 'Motion-JPEG (format B)',
#            SVQ1 =&gt; 'Sorenson video, version 1',
#            SVQ3 =&gt; 'Sorenson video, version 3',
#            mp4v =&gt; 'MPEG-4 video',
#           'dvc '=&gt; 'NTSC DV-25 video',
#            dvcp =&gt; 'PAL DV-25 video',
#           'gif '=&gt; 'Compuserve Graphics Interchange Format',
#            h263 =&gt; 'H.263 video',
#            tiff =&gt; 'Tagged Image File Format',
#           'raw '=&gt; 'Uncompressed RGB',
#           '2vuY'=&gt; "Uncompressed Y'CbCr, 3x8-bit 4:2:2 (2vuY)",
#           'yuv2'=&gt; "Uncompressed Y'CbCr, 3x8-bit 4:2:2 (yuv2)",
#            v308 =&gt; "Uncompressed Y'CbCr, 8-bit 4:4:4",
#            v408 =&gt; "Uncompressed Y'CbCr, 8-bit 4:4:4:4",
#            v216 =&gt; "Uncompressed Y'CbCr, 10, 12, 14, or 16-bit 4:2:2",
#            v410 =&gt; "Uncompressed Y'CbCr, 10-bit 4:4:4",
#            v210 =&gt; "Uncompressed Y'CbCr, 10-bit 4:2:2",
#        },
    },
    10 =&gt; {
        Name =&gt; 'VendorID',
        Format =&gt; 'string[4]',
        RawConv =&gt; 'length $val ? $val : undef',
        PrintConv =&gt; \%vendorID,
        SeparateTable =&gt; 'VendorID',
    },
  # 14 - ("Quality" in QuickTime docs) ??
    16 =&gt; 'SourceImageWidth',
    17 =&gt; 'SourceImageHeight',
    18 =&gt; { Name =&gt; 'XResolution',  Format =&gt; 'fixed32u' },
    20 =&gt; { Name =&gt; 'YResolution',  Format =&gt; 'fixed32u' },
  # 24 =&gt; 'FrameCount', # always 1 (what good is this?)
    25 =&gt; {
        Name =&gt; 'CompressorName',
        Format =&gt; 'string[32]',
        # (sometimes this is a Pascal string, and sometimes it is a C string)
        RawConv =&gt; q{
            $val=substr($val,1,ord($1)) if $val=~/^([\0-\x1f])/ and ord($1)&lt;length($val);
            length $val ? $val : undef;
        },
    },
    41 =&gt; 'BitDepth',
);

# preview data block
%Image::ExifTool::QuickTime::Preview = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    GROUPS =&gt; { 2 =&gt; 'Image' },
    FORMAT =&gt; 'int16u',
    0 =&gt; {
        Name =&gt; 'PreviewDate',
        Format =&gt; 'int32u',
        Groups =&gt; { 2 =&gt; 'Time' },
        %timeInfo,
    },
    2 =&gt; 'PreviewVersion',
    3 =&gt; {
        Name =&gt; 'PreviewAtomType',
        Format =&gt; 'string[4]',
    },
    5 =&gt; 'PreviewAtomIndex',
);

# movie atoms
%Image::ExifTool::QuickTime::Movie = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    mvhd =&gt; {
        Name =&gt; 'MovieHeader',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::MovieHeader' },
    },
    trak =&gt; {
        Name =&gt; 'Track',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::Track' },
    },
    udta =&gt; {
        Name =&gt; 'UserData',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::UserData' },
    },
    meta =&gt; { # 'meta' is found here in my EX-F1 MOV sample - PH
        Name =&gt; 'Meta',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::Meta' },
    },
    iods =&gt; {
        Name =&gt; 'InitialObjectDescriptor',
        Flags =&gt; ['Binary','Unknown'],
    },
    uuid =&gt; [
        { #11 (MP4 files) (also found in QuickTime::Track)
            Name =&gt; 'UUID-USMT',
            Condition =&gt; '$$valPt=~/^USMT!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::QuickTime::UserMedia',
                Start =&gt; 16,
            },
        },
        {
            Name =&gt; 'UUID-Unknown',
            Unknown =&gt; 1,
            Binary =&gt; 1,
        },
    ],
    # prfl - Profile (ref 12)
    # clip - clipping --&gt; contains crgn (clip region) (ref 12)
    # mvex - movie extends --&gt; contains mehd (movie extends header), trex (track extends) (ref 14)
);

# movie header data block
%Image::ExifTool::QuickTime::MovieHeader = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    FORMAT =&gt; 'int32u',
    0 =&gt; {
        Name =&gt; 'MovieHeaderVersion',
        Format =&gt; 'int8u',
        RawConv =&gt; '$$self{MovieHeaderVersion} = $val',
    },
    1 =&gt; {
        Name =&gt; 'CreateDate',
        Groups =&gt; { 2 =&gt; 'Time' },
        %timeInfo,
        # this is int64u if MovieHeaderVersion == 1 (ref 13)
        Hook =&gt; '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
    },
    2 =&gt; {
        Name =&gt; 'ModifyDate',
        Groups =&gt; { 2 =&gt; 'Time' },
        %timeInfo,
        # this is int64u if MovieHeaderVersion == 1 (ref 13)
        Hook =&gt; '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
    },
    3 =&gt; {
        Name =&gt; 'TimeScale',
        RawConv =&gt; '$$self{TimeScale} = $val',
    },
    4 =&gt; {
        Name =&gt; 'Duration',
        %durationInfo,
        # this is int64u if MovieHeaderVersion == 1 (ref 13)
        Hook =&gt; '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
    },
    5 =&gt; {
        Name =&gt; 'PreferredRate',
        ValueConv =&gt; '$val / 0x10000',
    },
    6 =&gt; {
        Name =&gt; 'PreferredVolume',
        Format =&gt; 'int16u',
        ValueConv =&gt; '$val / 256',
        PrintConv =&gt; 'sprintf("%.2f%%", $val * 100)',
    },
    9 =&gt; {
        Name =&gt; 'MatrixStructure',
        Format =&gt; 'fixed32s[9]',
        # (the right column is fixed 2.30 instead of 16.16)
        ValueConv =&gt; q{
            my @a = split ' ',$val;
            $_ /= 0x4000 foreach @a[2,5,8];
            return "@a";
        },
    },
    18 =&gt; { Name =&gt; 'PreviewTime',      %durationInfo },
    19 =&gt; { Name =&gt; 'PreviewDuration',  %durationInfo },
    20 =&gt; { Name =&gt; 'PosterTime',       %durationInfo },
    21 =&gt; { Name =&gt; 'SelectionTime',    %durationInfo },
    22 =&gt; { Name =&gt; 'SelectionDuration',%durationInfo },
    23 =&gt; { Name =&gt; 'CurrentTime',      %durationInfo },
    24 =&gt; 'NextTrackID',
);

# track atoms
%Image::ExifTool::QuickTime::Track = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    tkhd =&gt; {
        Name =&gt; 'TrackHeader',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::TrackHeader' },
    },
    udta =&gt; {
        Name =&gt; 'UserData',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::UserData' },
    },
    mdia =&gt; { #MP4
        Name =&gt; 'Media',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::Media' },
    },
    meta =&gt; { #PH (MOV)
        Name =&gt; 'Meta',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::Meta' },
    },
    tref =&gt; {
        Name =&gt; 'TrackRef',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::TrackRef' },
    },
    uuid =&gt; [
        { #11 (MP4 files) (also found in QuickTime::Movie)
            Name =&gt; 'UUID-USMT',
            Condition =&gt; '$$valPt=~/^USMT!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::QuickTime::UserMedia',
                Start =&gt; 16,
            },
        },
        {
            Name =&gt; 'UUID-Unknown',
            Unknown =&gt; 1,
            Binary =&gt; 1,
        },
    ],
    # edts - edits --&gt; contains elst (edit list)
    # tapt - TrackApertureModeDimensionsAID --&gt; contains clef (TrackCleanApertureDimensionsAID),
    #        prof (TrackProductionApertureDimensionsAID), enof (TrackEncodedPixelsDimensionsAID)
    # clip - clipping --&gt; contains crgn (clip region)
    # matt - track matt --&gt; contains kmat (compressed matt)
    # load - track loading settings
    # imap - track input map --&gt; contains '  in' --&gt; contains '  ty', obid
    # prfl - Profile (ref 12)
);

# track header data block
%Image::ExifTool::QuickTime::TrackHeader = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    GROUPS =&gt; { 1 =&gt; 'Track#', 2 =&gt; 'Video' },
    FORMAT =&gt; 'int32u',
    0 =&gt; {
        Name =&gt; 'TrackHeaderVersion',
        Format =&gt; 'int8u',
        Priority =&gt; 0,
        RawConv =&gt; '$$self{TrackHeaderVersion} = $val',
    },
    1 =&gt; {
        Name =&gt; 'TrackCreateDate',
        Priority =&gt; 0,
        Groups =&gt; { 2 =&gt; 'Time' },
        %timeInfo,
        # this is int64u if TrackHeaderVersion == 1 (ref 13)
        Hook =&gt; '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
    },
    2 =&gt; {
        Name =&gt; 'TrackModifyDate',
        Priority =&gt; 0,
        Groups =&gt; { 2 =&gt; 'Time' },
        %timeInfo,
        # this is int64u if TrackHeaderVersion == 1 (ref 13)
        Hook =&gt; '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
    },
    3 =&gt; {
        Name =&gt; 'TrackID',
        Priority =&gt; 0,
    },
    5 =&gt; {
        Name =&gt; 'TrackDuration',
        Priority =&gt; 0,
        %durationInfo,
        # this is int64u if TrackHeaderVersion == 1 (ref 13)
        Hook =&gt; '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
    },
    8 =&gt; {
        Name =&gt; 'TrackLayer',
        Format =&gt; 'int16u',
        Priority =&gt; 0,
    },
    9 =&gt; {
        Name =&gt; 'TrackVolume',
        Format =&gt; 'int16u',
        Priority =&gt; 0,
        ValueConv =&gt; '$val / 256',
        PrintConv =&gt; 'sprintf("%.2f%%", $val * 100)',
    },
    10 =&gt; {
        Name =&gt; 'MatrixStructure',
        Format =&gt; 'fixed32s[9]',
        # (the right column is fixed 2.30 instead of 16.16)
        ValueConv =&gt; q{
            my @a = split ' ',$val;
            $_ /= 0x4000 foreach @a[2,5,8];
            return "@a";
        },
    },
    19 =&gt; {
        Name =&gt; 'ImageWidth',
        Priority =&gt; 0,
        RawConv =&gt; \&amp;FixWrongFormat,
    },
    20 =&gt; {
        Name =&gt; 'ImageHeight',
        Priority =&gt; 0,
        RawConv =&gt; \&amp;FixWrongFormat,
    },
);

# user data atoms
%Image::ExifTool::QuickTime::UserData = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    NOTES =&gt; q{
        Tag ID's beginning with the copyright symbol (hex 0xa9) are multi-language
        text.  Alternate language tags are accessed by adding a dash followed by the
        language/country code to the tag name.  ExifTool will extract any
        multi-language user data tags found, even if they don't exist in this table.
    },
    "\xa9cpy" =&gt; { Name =&gt; 'Copyright',  Groups =&gt; { 2 =&gt; 'Author' } },
    "\xa9day" =&gt; {
        Name =&gt; 'CreateDate',
        Groups =&gt; { 2 =&gt; 'Time' },
        # handle values in the form "2010-02-12T13:27:14-0800" (written by Apple iPhone)
        ValueConv =&gt; q{
            require Image::ExifTool::XMP;
            $val =  Image::ExifTool::XMP::ConvertXMPDate($val);
            $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
            return $val;
        },
        PrintConv =&gt; '$self-&gt;ConvertDateTime($val)',
    },
    "\xa9ART" =&gt; 'Artist', #PH (iTunes 8.0.2)
    "\xa9alb" =&gt; 'Album', #PH (iTunes 8.0.2)
    "\xa9arg" =&gt; 'Arranger', #12
    "\xa9ark" =&gt; 'ArrangerKeywords', #12
    "\xa9cmt" =&gt; 'Comment', #PH (iTunes 8.0.2)
    "\xa9cok" =&gt; 'ComposerKeywords', #12
    "\xa9com" =&gt; 'Composer', #12
    "\xa9dir" =&gt; 'Director', #12
    "\xa9ed1" =&gt; 'Edit1',
    "\xa9ed2" =&gt; 'Edit2',
    "\xa9ed3" =&gt; 'Edit3',
    "\xa9ed4" =&gt; 'Edit4',
    "\xa9ed5" =&gt; 'Edit5',
    "\xa9ed6" =&gt; 'Edit6',
    "\xa9ed7" =&gt; 'Edit7',
    "\xa9ed8" =&gt; 'Edit8',
    "\xa9ed9" =&gt; 'Edit9',
    "\xa9fmt" =&gt; 'Format',
    "\xa9gen" =&gt; 'Genre', #PH (iTunes 8.0.2)
    "\xa9grp" =&gt; 'Grouping', #PH (NC)
    "\xa9inf" =&gt; 'Information',
    "\xa9isr" =&gt; 'ISRCCode', #12
    "\xa9lab" =&gt; 'RecordLabelName', #12
    "\xa9lal" =&gt; 'RecordLabelURL', #12
    "\xa9lyr" =&gt; 'Lyrics', #PH (NC)
    "\xa9mak" =&gt; 'Make', #12
    "\xa9mal" =&gt; 'MakerURL', #12
    "\xa9mod" =&gt; 'Model', #PH
    "\xa9nam" =&gt; 'Title', #12
    "\xa9pdk" =&gt; 'ProducerKeywords', #12
    "\xa9phg" =&gt; 'RecordingCopyright', #12
    "\xa9prd" =&gt; 'Producer',
    "\xa9prf" =&gt; 'Performers',
    "\xa9prk" =&gt; 'PerformerKeywords', #12
    "\xa9prl" =&gt; 'PerformerURL',
    "\xa9dir" =&gt; 'Director', #12
    "\xa9req" =&gt; 'Requirements',
    "\xa9snk" =&gt; 'SubtitleKeywords', #12
    "\xa9snm" =&gt; 'Subtitle', #12
    "\xa9src" =&gt; 'SourceCredits', #12
    "\xa9swf" =&gt; 'SongWriter', #12
    "\xa9swk" =&gt; 'SongWriterKeywords', #12
    "\xa9swr" =&gt; 'SoftwareVersion', #12
    "\xa9too" =&gt; 'Encoder', #PH (NC)
    "\xa9trk" =&gt; 'Track', #PH (NC)
    "\xa9wrt" =&gt; 'Composer',
    "\xa9xyz" =&gt; { #PH (iPhone 3GS)
        Name =&gt; 'GPSCoordinates',
        Groups =&gt; { 2 =&gt; 'Location' },
        ValueConv =&gt; \&amp;ConvertISO6709,
        PrintConv =&gt; \&amp;PrintGPSCoordinates,
    },
    name =&gt; 'Name',
    WLOC =&gt; {
        Name =&gt; 'WindowLocation',
        Format =&gt; 'int16u',
    },
    LOOP =&gt; {
        Name =&gt; 'LoopStyle',
        Format =&gt; 'int32u',
        PrintConv =&gt; {
            1 =&gt; 'Normal',
            2 =&gt; 'Palindromic',
        },
    },
    SelO =&gt; {
        Name =&gt; 'PlaySelection',
        Format =&gt; 'int8u',
    },
    AllF =&gt; {
        Name =&gt; 'PlayAllFrames',
        Format =&gt; 'int8u',
    },
    meta =&gt; {
        Name =&gt; 'Meta',
        SubDirectory =&gt; {
            TagTable =&gt; 'Image::ExifTool::QuickTime::Meta',
            Start =&gt; 4, # must skip 4-byte version number header
        },
    },
    DcMD =&gt; {
        Name =&gt; 'DcMD',
        SubDirectory =&gt; {
            TagTable =&gt; 'Image::ExifTool::QuickTime::DcMD',
        },
    },
   'ptv '=&gt; {
        Name =&gt; 'PrintToVideo',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::Video' },
    },
   'hnti'=&gt; {
        Name =&gt; 'HintInfo',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::HintInfo' },
    },
   'hinf' =&gt; {
        Name =&gt; 'HintTrackInfo',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::HintTrackInfo' },
    },
    TAGS =&gt; [ #PH
        # these tags were initially discovered in a Pentax movie,
        # but similar information is found in videos from other manufacturers
        {
            Name =&gt; 'KodakTags',
            Condition =&gt; '$$valPt =~ /^EASTMAN KODAK COMPANY/',
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::Kodak::MOV',
                ByteOrder =&gt; 'LittleEndian',
            },
        },
        {
            Name =&gt; 'KonicaMinoltaTags',
            Condition =&gt; '$$valPt =~ /^KONICA MINOLTA DIGITAL CAMERA/',
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::Minolta::MOV1',
                ByteOrder =&gt; 'LittleEndian',
            },
        },
        {
            Name =&gt; 'MinoltaTags',
            Condition =&gt; '$$valPt =~ /^MINOLTA DIGITAL CAMERA/',
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::Minolta::MOV2',
                ByteOrder =&gt; 'LittleEndian',
            },
        },
        {
            Name =&gt; 'NikonTags',
            Condition =&gt; '$$valPt =~ /^NIKON DIGITAL CAMERA\0/',
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::Nikon::MOV',
                ByteOrder =&gt; 'LittleEndian',
            },
        },
        {
            Name =&gt; 'OlympusTags1',
            Condition =&gt; '$$valPt =~ /^OLYMPUS DIGITAL CAMERA\0.{9}\x01\0/s',
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::Olympus::MOV1',
                ByteOrder =&gt; 'LittleEndian',
            },
        },
        {
            Name =&gt; 'OlympusTags2',
            Condition =&gt; '$$valPt =~ /^OLYMPUS DIGITAL CAMERA(?!\0.{21}\x0a\0{3})/s',
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::Olympus::MOV2',
                ByteOrder =&gt; 'LittleEndian',
            },
        },
        {
            Name =&gt; 'OlympusTags3',
            Condition =&gt; '$$valPt =~ /^OLYMPUS DIGITAL CAMERA\0/',
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::Olympus::MP4',
                ByteOrder =&gt; 'LittleEndian',
            },
        },
        {
            Name =&gt; 'PentaxTags',
            Condition =&gt; '$$valPt =~ /^PENTAX DIGITAL CAMERA\0/',
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::Pentax::MOV',
                ByteOrder =&gt; 'LittleEndian',
            },
        },
        {
            Name =&gt; 'SamsungTags',
            Condition =&gt; '$$valPt =~ /^SAMSUNG DIGITAL CAMERA\0/',
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::Samsung::MP4',
                ByteOrder =&gt; 'LittleEndian',
            },
        },
        {
            Name =&gt; 'SanyoMOV',
            Condition =&gt; q{
                $$valPt =~ /^SANYO DIGITAL CAMERA\0/ and
                $self-&gt;{VALUE}-&gt;{FileType} eq "MOV"
            },
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::Sanyo::MOV',
                ByteOrder =&gt; 'LittleEndian',
            },
        },
        {
            Name =&gt; 'SanyoMP4',
            Condition =&gt; q{
                $$valPt =~ /^SANYO DIGITAL CAMERA\0/ and
                $self-&gt;{VALUE}-&gt;{FileType} eq "MP4"
            },
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::Sanyo::MP4',
                ByteOrder =&gt; 'LittleEndian',
            },
        },
        {
            Name =&gt; 'UnknownTags',
            Unknown =&gt; 1,
            Binary =&gt; 1
        },
    ],
    NCDT =&gt; { #PH
        Name =&gt; 'NikonNCDT',
        SubDirectory =&gt; {
            TagTable =&gt; 'Image::ExifTool::Nikon::NCDT',
            ProcessProc =&gt; \&amp;ProcessMOV,
        },
    },
    QVMI =&gt; { #PH
        Name =&gt; 'CasioQVMI',
        # Casio stores standard EXIF-format information in MOV videos (ie. EX-S880)
        SubDirectory =&gt; {
            TagTable =&gt; 'Image::ExifTool::Exif::Main',
            DirName =&gt; 'IFD0',
            Multi =&gt; 0, # (no NextIFD pointer)
            Start =&gt; 10,
            ByteOrder =&gt; 'BigEndian',
        },
    },
    MMA0 =&gt; { #PH (DiMage 7Hi)
        Name =&gt; 'MinoltaMMA0',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::Minolta::MMA' },
    },
    MMA1 =&gt; { #PH (Dimage A2)
        Name =&gt; 'MinoltaMMA1',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::Minolta::MMA' },
    },
    XMP_ =&gt; { #PH (Adobe CS3 Bridge)
        Name =&gt; 'XMP',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::XMP::Main' },
    },
    vndr =&gt; 'Vendor', #PH (Samsung PL70)
    SDLN =&gt; 'PlayMode', #PH (NC, Samsung ST80 "SEQ_PLAY")
    # Canon tags
    CNCV =&gt; 'CompressorVersion', #PH (5D Mark II)
    CNMN =&gt; 'Model', #PH (EOS 550D)
    CNFV =&gt; 'FirmwareVersion', #PH (EOS 550D)
    CNTH =&gt; { #PH (PowerShot S95)
        Name =&gt; 'CanonCNTH',
        SubDirectory =&gt; {
            TagTable =&gt; 'Image::ExifTool::Canon::CNTH',
            ProcessProc =&gt; \&amp;ProcessMOV,
        },
    },
    # CNDB - 2112 bytes (550D)
    # CNDM - 4 bytes - 0xff,0xd8,0xff,0xd9 (S95)
    INFO =&gt; {
        Name =&gt; 'SamsungINFO',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::Samsung::INFO' },
    },
    FFMV =&gt; { #PH (FinePix HS20EXR)
        Name =&gt; 'FujiFilmFFMV',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::FujiFilm::FFMV' },
    },
    MVTG =&gt; { #PH (FinePix HS20EXR)
        Name =&gt; 'FujiFilmMVTG',
        SubDirectory =&gt; {
            TagTable =&gt; 'Image::ExifTool::Exif::Main',
            DirName =&gt; 'IFD0',
            Start =&gt; 16,
            Base =&gt; '$start',
            ByteOrder =&gt; 'LittleEndian',
        },
    },
);

# User-specific media data atoms (ref 11)
%Image::ExifTool::QuickTime::UserMedia = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    MTDT =&gt; {
        Name =&gt; 'MetaData',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::MetaData' },
    },
);

# User-specific media data atoms (ref 11)
%Image::ExifTool::QuickTime::MetaData = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::QuickTime::ProcessMetaData,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    TAG_PREFIX =&gt; 'MetaData',
    0x01 =&gt; 'Title',
    0x03 =&gt; {
        Name =&gt; 'ProductionDate',
        Groups =&gt; { 2 =&gt; 'Time' },
        # translate from format "YYYY/mm/dd HH:MM:SS"
        ValueConv =&gt; '$val=~tr{/}{:}; $val',
        PrintConv =&gt; '$self-&gt;ConvertDateTime($val)',
    },
    0x04 =&gt; 'Software',
    0x05 =&gt; 'Product',
    0x0a =&gt; {
        Name =&gt; 'TrackProperty',
        RawConv =&gt; 'my @a=unpack("Nnn",$val); "@a"',
        PrintConv =&gt; [
            { 0 =&gt; 'No presentation', BITMASK =&gt; { 0 =&gt; 'Main track' } },
            { 0 =&gt; 'No attributes',   BITMASK =&gt; { 15 =&gt; 'Read only' } },
            '"Priority $val"',
        ],
    },
    0x0b =&gt; {
        Name =&gt; 'TimeZone',
        Groups =&gt; { 2 =&gt; 'Time' },
        RawConv =&gt; 'Get16s(\$val,0)',
        PrintConv =&gt; 'TimeZoneString($val)',
    },
    0x0c =&gt; {
        Name =&gt; 'ModifyDate',
        Groups =&gt; { 2 =&gt; 'Time' },
        # translate from format "YYYY/mm/dd HH:MM:SS"
        ValueConv =&gt; '$val=~tr{/}{:}; $val',
        PrintConv =&gt; '$self-&gt;ConvertDateTime($val)',
    },
);

# Profile atoms (ref 11)
%Image::ExifTool::QuickTime::Profile = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    FPRF =&gt; {
        Name =&gt; 'FileGlobalProfile',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::FileProf' },
    },
    APRF =&gt; {
        Name =&gt; 'AudioProfile',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::AudioProf' },
    },
    VPRF =&gt; {
        Name =&gt; 'VideoProfile',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::VideoProf' },
    },
);

# FPRF atom information (ref 11)
%Image::ExifTool::QuickTime::FileProf = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    FORMAT =&gt; 'int32u',
    0 =&gt; { Name =&gt; 'FileProfileVersion', Unknown =&gt; 1 }, # unknown = uninteresting
    1 =&gt; {
        Name =&gt; 'FileFunctionFlags',
        PrintConv =&gt; { BITMASK =&gt; {
            28 =&gt; 'Fragmented',
            29 =&gt; 'Additional tracks',
            30 =&gt; 'Edited', # (main AV track is edited)
        }},
    },
    # 2 - reserved
);

# APRF atom information (ref 11)
%Image::ExifTool::QuickTime::AudioProf = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    GROUPS =&gt; { 2 =&gt; 'Audio' },
    FORMAT =&gt; 'int32u',
    0 =&gt; { Name =&gt; 'AudioProfileVersion', Unknown =&gt; 1 },
    1 =&gt; 'AudioTrackID',
    2 =&gt; {
        Name =&gt; 'AudioCodec',
        Format =&gt; 'undef[4]',
    },
    3 =&gt; {
        Name =&gt; 'AudioCodecInfo',
        Unknown =&gt; 1,
        PrintConv =&gt; 'sprintf("0x%.4x", $val)',
    },
    4 =&gt; {
        Name =&gt; 'AudioAttributes',
        PrintConv =&gt; { BITMASK =&gt; {
            0 =&gt; 'Encrypted',
            1 =&gt; 'Variable bitrate',
            2 =&gt; 'Dual mono',
        }},
    },
    5 =&gt; {
        Name =&gt; 'AudioAvgBitrate',
        ValueConv =&gt; '$val * 1000',
        PrintConv =&gt; 'ConvertBitrate($val)',
    },
    6 =&gt; {
        Name =&gt; 'AudioMaxBitrate',
        ValueConv =&gt; '$val * 1000',
        PrintConv =&gt; 'ConvertBitrate($val)',
    },
    7 =&gt; 'AudioSampleRate',
    8 =&gt; 'AudioChannels',
);

# VPRF atom information (ref 11)
%Image::ExifTool::QuickTime::VideoProf = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    FORMAT =&gt; 'int32u',
    0 =&gt; { Name =&gt; 'VideoProfileVersion', Unknown =&gt; 1 },
    1 =&gt; 'VideoTrackID',
    2 =&gt; {
        Name =&gt; 'VideoCodec',
        Format =&gt; 'undef[4]',
    },
    3 =&gt; {
        Name =&gt; 'VideoCodecInfo',
        Unknown =&gt; 1,
        PrintConv =&gt; 'sprintf("0x%.4x", $val)',
    },
    4 =&gt; {
        Name =&gt; 'VideoAttributes',
        PrintConv =&gt; { BITMASK =&gt; {
            0 =&gt; 'Encrypted',
            1 =&gt; 'Variable bitrate',
            2 =&gt; 'Variable frame rate',
            3 =&gt; 'Interlaced',
        }},
    },
    5 =&gt; {
        Name =&gt; 'VideoAvgBitrate',
        ValueConv =&gt; '$val * 1000',
        PrintConv =&gt; 'ConvertBitrate($val)',
    },
    6 =&gt; {
        Name =&gt; 'VideoMaxBitrate',
        ValueConv =&gt; '$val * 1000',
        PrintConv =&gt; 'ConvertBitrate($val)',
    },
    7 =&gt; {
        Name =&gt; 'VideoAvgFrameRate',
        Format =&gt; 'fixed32u',
        PrintConv =&gt; 'int($val * 1000 + 0.5) / 1000',
    },
    8 =&gt; {
        Name =&gt; 'VideoMaxFrameRate',
        Format =&gt; 'fixed32u',
        PrintConv =&gt; 'int($val * 1000 + 0.5) / 1000',
    },
    9 =&gt; {
        Name =&gt; 'VideoSize',
        Format =&gt; 'int16u[2]',
        PrintConv =&gt; '$val=~tr/ /x/; $val',
    },
    10 =&gt; {
        Name =&gt; 'PixelAspectRatio',
        Format =&gt; 'int16u[2]',
        PrintConv =&gt; '$val=~tr/ /:/; $val',
    },
);

# meta atoms
%Image::ExifTool::QuickTime::Meta = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    ilst =&gt; {
        Name =&gt; 'ItemList',
        SubDirectory =&gt; {
            TagTable =&gt; 'Image::ExifTool::QuickTime::ItemList',
            HasData =&gt; 1, # process atoms as containers with 'data' elements
        },
    },
    # MP4 tags (ref 5)
    hdlr =&gt; {
        Name =&gt; 'Handler',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::Handler' },
    },
    dinf =&gt; {
        Name =&gt; 'DataInformation',
        Flags =&gt; ['Binary','Unknown'],
    },
    ipmc =&gt; {
        Name =&gt; 'IPMPControl',
        Flags =&gt; ['Binary','Unknown'],
    },
    iloc =&gt; {
        Name =&gt; 'ItemLocation',
        Flags =&gt; ['Binary','Unknown'],
    },
    ipro =&gt; {
        Name =&gt; 'ItemProtection',
        Flags =&gt; ['Binary','Unknown'],
    },
    iinf =&gt; {
        Name =&gt; 'ItemInformation',
        Flags =&gt; ['Binary','Unknown'],
    },
   'xml ' =&gt; {
        Name =&gt; 'XML',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::XMP::Main' },
    },
   'keys' =&gt; {
        Name =&gt; 'Keys',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::Keys' },
    },
    bxml =&gt; {
        Name =&gt; 'BinaryXML',
        Flags =&gt; ['Binary','Unknown'],
    },
    pitm =&gt; {
        Name =&gt; 'PrimaryItemReference',
        Flags =&gt; ['Binary','Unknown'],
    },
    free =&gt; { #PH
        Name =&gt; 'Free',
        Flags =&gt; ['Binary','Unknown'],
    },
);

# track reference atoms
%Image::ExifTool::QuickTime::TrackRef = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    chap =&gt; { Name =&gt; 'ChapterList', Format =&gt; 'int32u' },
    # also: tmcd, sync, scpt, ssrc, iTunesInfo
);

# item list atoms
# -&gt; these atoms are unique, and contain one or more 'data' atoms
%Image::ExifTool::QuickTime::ItemList = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    GROUPS =&gt; { 2 =&gt; 'Audio' },
    NOTES =&gt; q{
        As well as these tags, the 'mdta' handler uses numerical tag ID's which are
        added dynamically to this table after processing the Meta Keys information.
    },
    # in this table, binary 1 and 2-byte "data"-type tags are interpreted as
    # int8u and int16u.  Multi-byte binary "data" tags are extracted as binary data
    "\xa9ART" =&gt; 'Artist',
    "\xa9alb" =&gt; 'Album',
    "\xa9cmt" =&gt; 'Comment',
    "\xa9com" =&gt; 'Composer',
    "\xa9day" =&gt; { Name =&gt; 'Year', Groups =&gt; { 2 =&gt; 'Time' } },
    "\xa9des" =&gt; 'Description', #4
    "\xa9enc" =&gt; 'EncodedBy', #10
    "\xa9gen" =&gt; 'Genre',
    "\xa9grp" =&gt; 'Grouping',
    "\xa9lyr" =&gt; 'Lyrics',
    "\xa9nam" =&gt; 'Title',
    # "\xa9st3" ? #10
    "\xa9too" =&gt; 'Encoder',
    "\xa9trk" =&gt; 'Track',
    "\xa9wrt" =&gt; 'Composer',
    '----' =&gt; {
        Name =&gt; 'iTunesInfo',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::iTunesInfo' },
    },
    aART =&gt; 'AlbumArtist',
    covr =&gt; 'CoverArt',
    cpil =&gt; { #10
        Name =&gt; 'Compilation',
        PrintConv =&gt; { 0 =&gt; 'No', 1 =&gt; 'Yes' },
    },
    disk =&gt; {
        Name =&gt; 'DiskNumber',
        ValueConv =&gt; 'length($val) &gt;= 6 ? join(" of ",unpack("x2nn",$val)) : \$val',
    },
    pgap =&gt; { #10
        Name =&gt; 'PlayGap',
        PrintConv =&gt; {
            0 =&gt; 'Insert Gap',
            1 =&gt; 'No Gap',
        },
    },
    tmpo =&gt; {
        Name =&gt; 'BeatsPerMinute',
        Format =&gt; 'int16u', # marked as boolean but really int16u in my sample
    },
    trkn =&gt; {
        Name =&gt; 'TrackNumber',
        ValueConv =&gt; 'length($val) &gt;= 6 ? join(" of ",unpack("x2nn",$val)) : \$val',
    },
#
# Note: it is possible that the tags below are not being decoded properly
# because I don't have samples to verify many of these - PH
#
    akID =&gt; { #10
        Name =&gt; 'AppleStoreAccountType',
        PrintConv =&gt; {
            0 =&gt; 'iTunes',
            1 =&gt; 'AOL',
        },
    },
    albm =&gt; 'Album', #(ffmpeg source)
    apID =&gt; 'AppleStoreAccount',
    atID =&gt; { #10 (or TV series)
        Name =&gt; 'AlbumTitleID',
        Format =&gt; 'int32u',
    },
    auth =&gt; { Name =&gt; 'Author', Groups =&gt; { 2 =&gt; 'Author' } },
    catg =&gt; 'Category', #7
    cnID =&gt; { #10
        Name =&gt; 'AppleStoreCatalogID',
        Format =&gt; 'int32u',
    },
    cprt =&gt; { Name =&gt; 'Copyright', Groups =&gt; { 2 =&gt; 'Author' } },
    dscp =&gt; 'Description',
    desc =&gt; 'Description', #7
    gnre =&gt; { #10
        Name =&gt; 'Genre',
        PrintConv =&gt; q{
            return $val unless $val =~ /^\d+$/;
            require Image::ExifTool::ID3;
            Image::ExifTool::ID3::PrintGenre($val - 1); # note the "- 1"
        },
    },
    egid =&gt; 'EpisodeGlobalUniqueID', #7
    geID =&gt; { #10
        Name =&gt; 'GenreID',
        Format =&gt; 'int32u',
        # 4005 = Kids
        # 4010 = Teens
    },
    grup =&gt; 'Grouping', #10
    hdvd =&gt; { #10
        Name =&gt; 'HDVideo',
        PrintConv =&gt; { 0 =&gt; 'No', 1 =&gt; 'Yes' },
    },
    keyw =&gt; 'Keyword', #7
    ldes =&gt; 'LongDescription', #10
    pcst =&gt; { #7
        Name =&gt; 'Podcast',
        PrintConv =&gt; { 0 =&gt; 'No', 1 =&gt; 'Yes' },
    },
    perf =&gt; 'Performer',
    plID =&gt; { #10 (or TV season)
        Name =&gt; 'PlayListID',
        Format =&gt; 'int8u',  # actually int64u, but split it up
    },
    purd =&gt; 'PurchaseDate', #7
    purl =&gt; 'PodcastURL', #7
    rtng =&gt; { #10
        Name =&gt; 'Rating',
        PrintConv =&gt; {
            0 =&gt; 'none',
            2 =&gt; 'Clean',
            4 =&gt; 'Explicit',
        },
    },
    sfID =&gt; { #10
        Name =&gt; 'AppleStoreCountry',
        Format =&gt; 'int32u',
        PrintConv =&gt; {
            143460 =&gt; 'Australia',
            143445 =&gt; 'Austria',
            143446 =&gt; 'Belgium',
            143455 =&gt; 'Canada',
            143458 =&gt; 'Denmark',
            143447 =&gt; 'Finland',
            143442 =&gt; 'France',
            143443 =&gt; 'Germany',
            143448 =&gt; 'Greece',
            143449 =&gt; 'Ireland',
            143450 =&gt; 'Italy',
            143462 =&gt; 'Japan',
            143451 =&gt; 'Luxembourg',
            143452 =&gt; 'Netherlands',
            143461 =&gt; 'New Zealand',
            143457 =&gt; 'Norway',
            143453 =&gt; 'Portugal',
            143454 =&gt; 'Spain',
            143456 =&gt; 'Sweden',
            143459 =&gt; 'Switzerland',
            143444 =&gt; 'United Kingdom',
            143441 =&gt; 'United States',
        },
    },
    soaa =&gt; 'SortAlbumArtist', #10
    soal =&gt; 'SortAlbum', #10
    soar =&gt; 'SortArtist', #10
    soco =&gt; 'SortComposer', #10
    sonm =&gt; 'SortName', #10
    sosn =&gt; 'SortShow', #10
    stik =&gt; { #10
        Name =&gt; 'MediaType',
        PrintConv =&gt; { #(http://weblog.xanga.com/gryphondwb/615474010/iphone-ringtones---what-did-itunes-741-really-do.html)
            0 =&gt; 'Movie',
            1 =&gt; 'Normal (Music)',
            2 =&gt; 'Audiobook',
            5 =&gt; 'Whacked Bookmark',
            6 =&gt; 'Music Video',
            9 =&gt; 'Short Film',
            10 =&gt; 'TV Show',
            11 =&gt; 'Booklet',
            14 =&gt; 'Ringtone',
        },
    },
    titl =&gt; 'Title',
    tven =&gt; 'TVEpisodeID', #7
    tves =&gt; { #7/10
        Name =&gt; 'TVEpisode',
        Format =&gt; 'int32u',
    },
    tvnn =&gt; 'TVNetworkName', #7
    tvsh =&gt; 'TVShow', #10
    tvsn =&gt; { #7/10
        Name =&gt; 'TVSeason',
        Format =&gt; 'int32u',
    },
    yrrc =&gt; 'Year', #(ffmpeg source)
);

# item list keys (ref PH)
%Image::ExifTool::QuickTime::Keys = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::QuickTime::ProcessKeys,
    NOTES =&gt; q{
        This directory contains a list of key names which are used to decode
        ItemList tags written by the "mdta" handler.  The prefix of
        "com.apple.quicktime." has been removed from all TagID's below.
    },
    version     =&gt; 'Version',
    album       =&gt; 'Album',
    artist      =&gt; { },
    artwork     =&gt; { },
    author      =&gt; { Name =&gt; 'Author',      Groups =&gt; { 2 =&gt; 'Author' } },
    comment     =&gt; { },
    copyright   =&gt; { Name =&gt; 'Copyright',   Groups =&gt; { 2 =&gt; 'Author' } },
    creationdate=&gt; {
        Name =&gt; 'CreationDate',
        Groups =&gt; { 2 =&gt; 'Time' },
        ValueConv =&gt; q{
            require Image::ExifTool::XMP;
            $val =  Image::ExifTool::XMP::ConvertXMPDate($val,1);
            $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
            return $val;
        },
        PrintConv =&gt; '$self-&gt;ConvertDateTime($val)',
    },
    description =&gt; { },
    director    =&gt; { },
    genre       =&gt; { },
    information =&gt; { },
    keywords    =&gt; { },
    make        =&gt; { Name =&gt; 'Make',        Groups =&gt; { 2 =&gt; 'Camera' } },
    model       =&gt; { Name =&gt; 'Model',       Groups =&gt; { 2 =&gt; 'Camera' } },
    publisher   =&gt; { },
    software    =&gt; { },
    year        =&gt; { Groups =&gt; { 2 =&gt; 'Time' } },
    'camera.identifier' =&gt; 'CameraIdentifier', # (iPhone 4)
    'camera.framereadouttimeinmicroseconds' =&gt; { # (iPhone 4)
        Name =&gt; 'FrameReadoutTime',
        ValueConv =&gt; '$val * 1e-6',
        PrintConv =&gt; '$val * 1e6 . " microseconds"',
    },
    'location.ISO6709' =&gt; {
        Name =&gt; 'GPSCoordinates',
        Groups =&gt; { 2 =&gt; 'Location' },
        ValueConv =&gt; \&amp;ConvertISO6709,
        PrintConv =&gt; \&amp;PrintGPSCoordinates,
    },
    'location.name' =&gt; { Name =&gt; 'LocationName', Groups =&gt; { 2 =&gt; 'Location' } },
    'location.body' =&gt; { Name =&gt; 'LocationBody', Groups =&gt; { 2 =&gt; 'Location' } },
    'location.note' =&gt; { Name =&gt; 'LocationNote', Groups =&gt; { 2 =&gt; 'Location' } },
    'location.role' =&gt; {
        Name =&gt; 'LocationRole',
        Groups =&gt; { 2 =&gt; 'Location' },
        PrintConv =&gt; {
            0 =&gt; 'Shooting Location',
            1 =&gt; 'Real Location',
            2 =&gt; 'Fictional Location',
        },
    },
    'location.date' =&gt; {
        Name =&gt; 'LocationDate',
        Groups =&gt; { 2 =&gt; 'Time' },
        ValueConv =&gt; q{
            require Image::ExifTool::XMP;
            $val =  Image::ExifTool::XMP::ConvertXMPDate($val);
            $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
            return $val;
        },
        PrintConv =&gt; '$self-&gt;ConvertDateTime($val)',
    },
    'direction.facing' =&gt; { Name =&gt; 'CameraDirection', Groups =&gt; { 2 =&gt; 'Location' } },
    'direction.motion' =&gt; { Name =&gt; 'CameraMotion', Groups =&gt; { 2 =&gt; 'Location' } },
    'location.body' =&gt; { Name =&gt; 'LocationBody', Groups =&gt; { 2 =&gt; 'Location' } },
    'player.version'                =&gt; 'PlayerVersion',
    'player.movie.visual.brightness'=&gt; 'Brightness',
    'player.movie.visual.color'     =&gt; 'Color',
    'player.movie.visual.tint'      =&gt; 'Tint',
    'player.movie.visual.contrast'  =&gt; 'Contrast',
    'player.movie.audio.gain'       =&gt; 'AudioGain',
    'player.movie.audio.treble'     =&gt; 'Trebel',
    'player.movie.audio.bass'       =&gt; 'Bass',
    'player.movie.audio.balance'    =&gt; 'Balance',
    'player.movie.audio.pitchshift' =&gt; 'PitchShift',
    'player.movie.audio.mute' =&gt; {
        Name =&gt; 'Mute',
        Format =&gt; 'int8u',
        PrintConv =&gt; { 0 =&gt; 'Off', 1 =&gt; 'On' },
    },
);

# iTunes info ('----') atoms
%Image::ExifTool::QuickTime::iTunesInfo = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    GROUPS =&gt; { 2 =&gt; 'Audio' },
    # 'mean'/'name'/'data' atoms form a triplet, but unfortunately
    # I can't find any source for decoding 'data'.
    # 'mean' is normally 'com.apple.iTunes'
    # 'name' values: 'tool', 'iTunNORM' (volume normalization),
    #   'iTunSMPB', 'iTunes_CDDB_IDs', 'iTunes_CDDB_TrackNumber'
    mean =&gt; {
        Name =&gt; 'Mean',
        Unknown =&gt; 1,
    },
    name =&gt; {
        Name =&gt; 'Name',
        Unknown =&gt; 1,
    },
    data =&gt; {
        Name =&gt; 'Data',
        Flags =&gt; ['Binary','Unknown'],
    },
);

# print to video data block
%Image::ExifTool::QuickTime::Video = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    0 =&gt; {
        Name =&gt; 'DisplaySize',
        PrintConv =&gt; {
            0 =&gt; 'Normal',
            1 =&gt; 'Double Size',
            2 =&gt; 'Half Size',
            3 =&gt; 'Full Screen',
            4 =&gt; 'Current Size',
        },
    },
    6 =&gt; {
        Name =&gt; 'SlideShow',
        PrintConv =&gt; {
            0 =&gt; 'No',
            1 =&gt; 'Yes',
        },
    },
);

# 'hnti' atoms
%Image::ExifTool::QuickTime::HintInfo = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    'rtp ' =&gt; {
        Name =&gt; 'RealtimeStreamingProtocol',
        PrintConv =&gt; '$val=~s/^sdp /(SDP) /; $val',
    },
    'sdp ' =&gt; 'StreamingDataProtocol',
);

# 'hinf' atoms
%Image::ExifTool::QuickTime::HintTrackInfo = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    trpY =&gt; { Name =&gt; 'TotalBytes', Format =&gt; 'int64u' }, #(documented)
    trpy =&gt; { Name =&gt; 'TotalBytes', Format =&gt; 'int64u' }, #(observed)
    totl =&gt; { Name =&gt; 'TotalBytes', Format =&gt; 'int32u' },
    nump =&gt; { Name =&gt; 'NumPackets', Format =&gt; 'int64u' },
    npck =&gt; { Name =&gt; 'NumPackets', Format =&gt; 'int32u' },
    tpyl =&gt; { Name =&gt; 'TotalBytesNoRTPHeaders', Format =&gt; 'int64u' },
    tpaY =&gt; { Name =&gt; 'TotalBytesNoRTPHeaders', Format =&gt; 'int32u' }, #(documented)
    tpay =&gt; { Name =&gt; 'TotalBytesNoRTPHeaders', Format =&gt; 'int32u' }, #(observed)
    maxr =&gt; {
        Name =&gt; 'MaxDataRate',
        Format =&gt; 'int32u',
        Count =&gt; 2,
        PrintConv =&gt; 'my @a=split(" ",$val);sprintf("%d bytes in %.3f s",$a[1],$a[0]/1000)',
    },
    dmed =&gt; { Name =&gt; 'MediaTrackBytes',    Format =&gt; 'int64u' },
    dimm =&gt; { Name =&gt; 'ImmediateDataBytes', Format =&gt; 'int64u' },
    drep =&gt; { Name =&gt; 'RepeatedDataBytes',  Format =&gt; 'int64u' },
    tmin =&gt; {
        Name =&gt; 'MinTransmissionTime',
        Format =&gt; 'int32u',
        PrintConv =&gt; 'sprintf("%.3f s",$val/1000)',
    },
    tmax =&gt; {
        Name =&gt; 'MaxTransmissionTime',
        Format =&gt; 'int32u',
        PrintConv =&gt; 'sprintf("%.3f s",$val/1000)',
    },
    pmax =&gt; { Name =&gt; 'LargestPacketSize',  Format =&gt; 'int32u' },
    dmax =&gt; {
        Name =&gt; 'LargestPacketDuration',
        Format =&gt; 'int32u',
        PrintConv =&gt; 'sprintf("%.3f s",$val/1000)',
    },
    payt =&gt; {
        Name =&gt; 'PayloadType',
        ValueConv =&gt; 'unpack("N",$val) . " " . substr($val, 5)',
        PrintConv =&gt; '$val=~s/ /, /;$val',
    },
);

# Kodak DcMD atoms (ref PH)
%Image::ExifTool::QuickTime::DcMD = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    NOTES =&gt; 'Metadata directory found in MOV videos from some Kodak cameras.',
    Cmbo =&gt; {
        Name =&gt; 'CameraByteOrder',
        PrintConv =&gt; {
            II =&gt; 'Little-endian (Intel, II)',
            MM =&gt; 'Big-endian (Motorola, MM)',
        },
    },
    DcME =&gt; {
        Name =&gt; 'DcME',
        SubDirectory =&gt; {
            TagTable =&gt; 'Image::ExifTool::QuickTime::DcME',
        },
    },
);

# Kodak DcME atoms (ref PH)
%Image::ExifTool::QuickTime::DcME = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    # Mtmd = binary data ("00 00 00 00 00 00 00 01" x 3)
    # Keyw = keywords? (six bytes all zero)
    # Rate = 2 bytes "00 00"
);

# MP4 media box (ref 5)
%Image::ExifTool::QuickTime::Media = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    NOTES =&gt; 'MP4 media box.',
    mdhd =&gt; {
        Name =&gt; 'MediaHeader',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::MediaHeader' },
    },
    hdlr =&gt; {
        Name =&gt; 'Handler',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::Handler' },
    },
    minf =&gt; {
        Name =&gt; 'MediaInfo',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::MediaInfo' },
    },
);

# MP4 media header box (ref 5)
%Image::ExifTool::QuickTime::MediaHeader = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    FORMAT =&gt; 'int32u',
    0 =&gt; {
        Name =&gt; 'MediaHeaderVersion',
        RawConv =&gt; '$$self{MediaHeaderVersion} = $val',
    },
    1 =&gt; {
        Name =&gt; 'MediaCreateDate',
        Groups =&gt; { 2 =&gt; 'Time' },
        %timeInfo,
        # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
        Hook =&gt; '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
    },
    2 =&gt; {
        Name =&gt; 'MediaModifyDate',
        Groups =&gt; { 2 =&gt; 'Time' },
        %timeInfo,
        # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
        Hook =&gt; '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
    },
    3 =&gt; {
        Name =&gt; 'MediaTimeScale',
        RawConv =&gt; '$$self{MediaTS} = $val',
    },
    4 =&gt; {
        Name =&gt; 'MediaDuration',
        RawConv =&gt; '$$self{MediaTS} ? $val / $$self{MediaTS} : $val',
        PrintConv =&gt; '$$self{MediaTS} ? ConvertDuration($val) : $val',
        # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
        Hook =&gt; '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
    },
    5 =&gt; {
        Name =&gt; 'MediaLanguageCode',
        Format =&gt; 'int16u',
        RawConv =&gt; '$val ? $val : undef',
        # allow both Macintosh (for MOV files) and ISO (for MP4 files) language codes
        ValueConv =&gt; '$val &lt; 0x400 ? $val : pack "C*", map { (($val&gt;&gt;$_)&amp;0x1f)+0x60 } 10, 5, 0',
        PrintConv =&gt; q{
            return $val unless $val =~ /^\d+$/;
            require Image::ExifTool::Font;
            return $Image::ExifTool::Font::ttLang{Macintosh}{$val} || "Unknown ($val)";
        },
    },
);

# MP4 media information box (ref 5)
%Image::ExifTool::QuickTime::MediaInfo = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    NOTES =&gt; 'MP4 media info box.',
    vmhd =&gt; {
        Name =&gt; 'VideoHeader',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::VideoHeader' },
    },
    smhd =&gt; {
        Name =&gt; 'AudioHeader',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::AudioHeader' },
    },
    hmhd =&gt; {
        Name =&gt; 'HintHeader',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::HintHeader' },
    },
    nmhd =&gt; {
        Name =&gt; 'NullMediaHeader',
        Flags =&gt; ['Binary','Unknown'],
    },
    dinf =&gt; {
        Name =&gt; 'DataInfo',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::DataInfo' },
    },
    gmhd =&gt; {
        Name =&gt; 'GenMediaHeader',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::GenMediaHeader' },
    },
    hdlr =&gt; { #PH
        Name =&gt; 'Handler',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::Handler' },
    },
    stbl =&gt; {
        Name =&gt; 'SampleTable',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::SampleTable' },
    },
);

# MP4 video media header (ref 5)
%Image::ExifTool::QuickTime::VideoHeader = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    NOTES =&gt; 'MP4 video media header.',
    FORMAT =&gt; 'int16u',
    2 =&gt; {
        Name =&gt; 'GraphicsMode',
        PrintHex =&gt; 1,
        SeparateTable =&gt; 'GraphicsMode',
        PrintConv =&gt; \%graphicsMode,
    },
    3 =&gt; { Name =&gt; 'OpColor', Format =&gt; 'int16u[3]' },
);

# MP4 audio media header (ref 5)
%Image::ExifTool::QuickTime::AudioHeader = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    GROUPS =&gt; { 2 =&gt; 'Audio' },
    NOTES =&gt; 'MP4 audio media header.',
    FORMAT =&gt; 'int16u',
    2 =&gt; { Name =&gt; 'Balance', Format =&gt; 'fixed16s' },
);

# MP4 hint media header (ref 5)
%Image::ExifTool::QuickTime::HintHeader = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    NOTES =&gt; 'MP4 hint media header.',
    FORMAT =&gt; 'int16u',
    2 =&gt; 'MaxPDUSize',
    3 =&gt; 'AvgPDUSize',
    4 =&gt; { Name =&gt; 'MaxBitrate', Format =&gt; 'int32u', PrintConv =&gt; 'ConvertBitrate($val)' },
    6 =&gt; { Name =&gt; 'AvgBitrate', Format =&gt; 'int32u', PrintConv =&gt; 'ConvertBitrate($val)' },
);

# MP4 sample table box (ref 5)
%Image::ExifTool::QuickTime::SampleTable = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    NOTES =&gt; 'MP4 sample table box.',
    stsd =&gt; [
        {
            Name =&gt; 'AudioSampleDesc',
            Condition =&gt; '$$self{HandlerType} and $$self{HandlerType} eq "soun"',
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::QuickTime::AudioSampleDesc',
                Start =&gt; 8, # skip version number and count
            },
        },{
            Name =&gt; 'VideoSampleDesc',
            Condition =&gt; '$$self{HandlerType} and $$self{HandlerType} eq "vide"',
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::QuickTime::ImageDesc',
                Start =&gt; 8, # skip version number and count
            },
        },{
            Name =&gt; 'HintSampleDesc',
            Condition =&gt; '$$self{HandlerType} and $$self{HandlerType} eq "hint"',
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::QuickTime::HintSampleDesc',
                Start =&gt; 8, # skip version number and count
            },
        },{
            Name =&gt; 'OtherSampleDesc',
            SubDirectory =&gt; {
                TagTable =&gt; 'Image::ExifTool::QuickTime::OtherSampleDesc',
                Start =&gt; 8, # skip version number and count
            },
        },
        # (Note: "alis" HandlerType handled by the parent audio or video handler)
    ],
    stts =&gt; [ # decoding time-to-sample table
        {
            Name =&gt; 'VideoFrameRate',
            Notes =&gt; 'average rate calculated from time-to-sample table for video media',
            Condition =&gt; '$$self{HandlerType} and $$self{HandlerType} eq "vide"',
            # (must be RawConv so appropriate MediaTS is used in calculation)
            RawConv =&gt; 'Image::ExifTool::QuickTime::CalcSampleRate($self, \$val)',
            PrintConv =&gt; 'int($val * 1000 + 0.5) / 1000',
        },
        {
            Name =&gt; 'TimeToSampleTable',
            Flags =&gt; ['Binary','Unknown'],
        },
    ],
    ctts =&gt; {
        Name =&gt; 'CompositionTimeToSample',
        Flags =&gt; ['Binary','Unknown'],
    },
    stsc =&gt; {
        Name =&gt; 'SampleToChunk',
        Flags =&gt; ['Binary','Unknown'],
    },
    stsz =&gt; {
        Name =&gt; 'SampleSizes',
        Flags =&gt; ['Binary','Unknown'],
    },
    stz2 =&gt; {
        Name =&gt; 'CompactSampleSizes',
        Flags =&gt; ['Binary','Unknown'],
    },
    stco =&gt; {
        Name =&gt; 'ChunkOffset',
        Flags =&gt; ['Binary','Unknown'],
    },
    co64 =&gt; {
        Name =&gt; 'ChunkOffset64',
        Flags =&gt; ['Binary','Unknown'],
    },
    stss =&gt; {
        Name =&gt; 'SyncSampleTable',
        Flags =&gt; ['Binary','Unknown'],
    },
    stsh =&gt; {
        Name =&gt; 'ShadowSyncSampleTable',
        Flags =&gt; ['Binary','Unknown'],
    },
    padb =&gt; {
        Name =&gt; 'SamplePaddingBits',
        Flags =&gt; ['Binary','Unknown'],
    },
    stdp =&gt; {
        Name =&gt; 'SampleDegradationPriority',
        Flags =&gt; ['Binary','Unknown'],
    },
    sdtp =&gt; {
        Name =&gt; 'IdependentAndDisposableSamples',
        Flags =&gt; ['Binary','Unknown'],
    },
    sbgp =&gt; {
        Name =&gt; 'SampleToGroup',
        Flags =&gt; ['Binary','Unknown'],
    },
    sgpd =&gt; {
        Name =&gt; 'SampleGroupDescription',
        Flags =&gt; ['Binary','Unknown'],
    },
    subs =&gt; {
        Name =&gt; 'Sub-sampleInformation',
        Flags =&gt; ['Binary','Unknown'],
    },
    cslg =&gt; {
        Name =&gt; 'CompositionToDecodeTimelineMapping',
        Flags =&gt; ['Binary','Unknown'],
    },
);

# MP4 audio sample description box (ref 5)
%Image::ExifTool::QuickTime::AudioSampleDesc = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    GROUPS =&gt; { 2 =&gt; 'Audio' },
    FORMAT =&gt; 'int16u',
    NOTES =&gt; 'MP4 audio sample description.',
    2  =&gt; {
        Name =&gt; 'AudioFormat',
        Format =&gt; 'undef[4]',
        RawConv =&gt; q{
            return undef unless $val =~ /^[\w ]{4}$/i;
            # check for protected audio format
            $self-&gt;OverrideFileType('M4P') if $val eq 'drms' and $$self{VALUE}{FileType} eq 'M4A';
            return $val;
        },
    },
    10 =&gt; { #PH
        Name =&gt; 'AudioVendorID',
        Format =&gt; 'undef[4]',
        RawConv =&gt; '$val eq "\0\0\0\0" ? undef : $val',
        PrintConv =&gt; \%vendorID,
        SeparateTable =&gt; 'VendorID',
    },
    12 =&gt; 'AudioChannels',
    13 =&gt; 'AudioBitsPerSample',
    16 =&gt; { Name =&gt; 'AudioSampleRate', Format =&gt; 'fixed32u' },
    28 =&gt; { #PH
        Name =&gt; 'AudioFormat',
        Format =&gt; 'undef[4]',
        RawConv =&gt; '$val =~ /^[\w ]{4}$/i ? $val : undef',
        Notes =&gt; 'in Casio MOV videos',
    },
);

# MP4 hint sample description box (ref 5)
%Image::ExifTool::QuickTime::HintSampleDesc = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    FORMAT =&gt; 'int16u',
    NOTES =&gt; 'MP4 hint sample description.',
    2 =&gt; { Name =&gt; 'HintFormat', Format =&gt; 'undef[4]' },
);

# MP4 generic sample description box
%Image::ExifTool::QuickTime::OtherSampleDesc = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    FORMAT =&gt; 'int16u',
    2 =&gt; { Name =&gt; 'OtherFormat', Format =&gt; 'undef[4]' },
);

# MP4 data information box (ref 5)
%Image::ExifTool::QuickTime::DataInfo = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    NOTES =&gt; 'MP4 data information box.',
    dref =&gt; {
        Name =&gt; 'DataRef',
        SubDirectory =&gt; {
            TagTable =&gt; 'Image::ExifTool::QuickTime::DataRef',
            Start =&gt; 8,
        },
    },
);

# Generic media header
%Image::ExifTool::QuickTime::GenMediaHeader = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    gmin =&gt; {
        Name =&gt; 'GenMediaInfo',
        SubDirectory =&gt; { TagTable =&gt; 'Image::ExifTool::QuickTime::GenMediaInfo' },
    },
    text =&gt; {
        Name =&gt; 'Text',
        Flags =&gt; ['Binary','Unknown'],
    },
);

# Generic media info (ref http://sourceforge.jp/cvs/view/ntvrec/ntvrec/libqtime/gmin.h?view=co)
%Image::ExifTool::QuickTime::GenMediaInfo = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    0  =&gt; 'GenMediaVersion',
    1  =&gt; { Name =&gt; 'GenFlags',   Format =&gt; 'int8u[3]' },
    4  =&gt; { Name =&gt; 'GenGraphicsMode',
        Format =&gt; 'int16u',
        PrintHex =&gt; 1,
        SeparateTable =&gt; 'GraphicsMode',
        PrintConv =&gt; \%graphicsMode,
    },
    6  =&gt; { Name =&gt; 'GenOpColor', Format =&gt; 'int16u[3]' },
    12 =&gt; { Name =&gt; 'GenBalance', Format =&gt; 'fixed16s' },
);

# MP4 data reference box (ref 5)
%Image::ExifTool::QuickTime::DataRef = (
    PROCESS_PROC =&gt; \&amp;ProcessMOV,
    NOTES =&gt; 'MP4 data reference box.',
    'url ' =&gt; {
        Name =&gt; 'URL',
        RawConv =&gt; q{
            # ignore if self-contained (flags bit 0 set)
            return undef if unpack("N",$val) &amp; 0x01;
            $_ = substr($val,4); s/\0.*//s; $_;
        },
    },
    'urn ' =&gt; {
        Name =&gt; 'URN',
        RawConv =&gt; q{
            return undef if unpack("N",$val) &amp; 0x01;
            $_ = substr($val,4); s/\0.*//s; $_;
        },
    },
);

# MP4 handler box (ref 5)
%Image::ExifTool::QuickTime::Handler = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    GROUPS =&gt; { 2 =&gt; 'Video' },
    4 =&gt; { #PH
        Name =&gt; 'HandlerClass',
        Format =&gt; 'undef[4]',
        RawConv =&gt; '$val eq "\0\0\0\0" ? undef : $val',
        PrintConv =&gt; {
            mhlr =&gt; 'Media Handler',
            dhlr =&gt; 'Data Handler',
        },
    },
    8 =&gt; {
        Name =&gt; 'HandlerType',
        Format =&gt; 'undef[4]',
        RawConv =&gt; '$$self{HandlerType} = $val unless $val eq "alis"; $val',
        PrintConv =&gt; {
            alis =&gt; 'Alias Data', #PH
            crsm =&gt; 'Clock Reference', #3
            hint =&gt; 'Hint Track',
            ipsm =&gt; 'IPMP', #3
            m7sm =&gt; 'MPEG-7 Stream', #3
            mdir =&gt; 'Metadata', #3
            mdta =&gt; 'Metadata Tags', #PH
            mjsm =&gt; 'MPEG-J', #3
            ocsm =&gt; 'Object Content', #3
            odsm =&gt; 'Object Descriptor', #3
            sdsm =&gt; 'Scene Description', #3
            soun =&gt; 'Audio Track',
            text =&gt; 'Text', #PH (but what type? subtitle?)
           'url '=&gt; 'URL', #3
            vide =&gt; 'Video Track',
        },
    },
    12 =&gt; { #PH
        Name =&gt; 'HandlerVendorID',
        Format =&gt; 'undef[4]',
        RawConv =&gt; '$val eq "\0\0\0\0" ? undef : $val',
        PrintConv =&gt; \%vendorID,
        SeparateTable =&gt; 'VendorID',
    },
    24 =&gt; {
        Name =&gt; 'HandlerDescription',
        Format =&gt; 'string',
        # (sometimes this is a Pascal string, and sometimes it is a C string)
        RawConv =&gt; q{
            $val=substr($val,1,ord($1)) if $val=~/^([\0-\x1f])/ and ord($1)&lt;length($val);
            length $val ? $val : undef;
        },
    },
);

# Flip uuid data (ref PH)
%Image::ExifTool::QuickTime::Flip = (
    PROCESS_PROC =&gt; \&amp;Image::ExifTool::ProcessBinaryData,
    FORMAT =&gt; 'int32u',
    FIRST_ENTRY =&gt; 0,
    NOTES =&gt; 'Found in MP4 files from Flip Video cameras.',
    GROUPS =&gt; { 1 =&gt; 'MakerNotes', 2 =&gt; 'Image' },
    1 =&gt; 'PreviewImageWidth',
    2 =&gt; 'PreviewImageHeight',
    13 =&gt; 'PreviewImageLength',
    14 =&gt; { # (confirmed for FlipVideoMinoHD)
        Name =&gt; 'SerialNumber',
        Groups =&gt; { 2 =&gt; 'Camera' },
        Format =&gt; 'string[16]',
    },
    28 =&gt; {
        Name =&gt; 'PreviewImage',
        Format =&gt; 'undef[$val{13}]',
        RawConv =&gt; '$self-&gt;ValidateImage(\$val, $tag)',
    },
);

# QuickTime composite tags
%Image::ExifTool::QuickTime::Composite = (
    GROUPS =&gt; { 2 =&gt; 'Video' },
    Rotation =&gt; {
        Require =&gt; {
            0 =&gt; 'QuickTime:MatrixStructure',
            1 =&gt; 'QuickTime:HandlerType',
        },
        ValueConv =&gt; 'Image::ExifTool::QuickTime::CalcRotation($self)',
    },
    AvgBitrate =&gt; {
        Priority =&gt; 0,  # let QuickTime::AvgBitrate take priority
        Require =&gt; {
            0 =&gt; 'QuickTime::MovieDataSize',
            1 =&gt; 'QuickTime::Duration',
        },
        RawConv =&gt; q{
            return undef unless $val[1];
            $val[1] /= $$self{TimeScale} if $$self{TimeScale};
            return int($val[0] * 8 / $val[1] + 0.5);
        },
        PrintConv =&gt; 'ConvertBitrate($val)',
    },
    GPSLatitude =&gt; {
        Require =&gt; 'QuickTime:GPSCoordinates',
        Groups =&gt; { 2 =&gt; 'Location' },
        ValueConv =&gt; 'my @c = split " ", $val; $c[0]',
        PrintConv =&gt; q{
            require Image::ExifTool::GPS;
            Image::ExifTool::GPS::ToDMS($self, $val, 1, 'N');
        },
    },
    GPSLongitude =&gt; {
        Require =&gt; 'QuickTime:GPSCoordinates',
        Groups =&gt; { 2 =&gt; 'Location' },
        ValueConv =&gt; 'my @c = split " ", $val; $c[1]',
        PrintConv =&gt; q{
            require Image::ExifTool::GPS;
            Image::ExifTool::GPS::ToDMS($self, $val, 1, 'E');
        },
    },
    # split altitude into GPSAltitude/GPSAltitudeRef like EXIF and XMP
    GPSAltitude =&gt; {
        Require =&gt; 'QuickTime:GPSCoordinates',
        Groups =&gt; { 2 =&gt; 'Location' },
        Priority =&gt; 0, # (because it may not exist)
        ValueConv =&gt; 'my @c = split " ", $val; defined $c[2] ? abs($c[2]) : undef',
        PrintConv =&gt; '"$val m"',
    },
    GPSAltitudeRef  =&gt; {
        Require =&gt; 'QuickTime:GPSCoordinates',
        Groups =&gt; { 2 =&gt; 'Location' },
        Priority =&gt; 0, # (because altitude information may not exist)
        ValueConv =&gt; 'my @c = split " ", $val; defined $c[2] ? ($c[2] &lt; 0 ? 1 : 0) : undef',
        PrintConv =&gt; {
            0 =&gt; 'Above Sea Level',
            1 =&gt; 'Below Sea Level',
        },
    },
);

# add our composite tags
Image::ExifTool::AddCompositeTags('Image::ExifTool::QuickTime');


#------------------------------------------------------------------------------
# Calculate rotation of video track
# Inputs: 0) ExifTool object ref
# Returns: rotation angle or undef
sub CalcRotation($)
{
    my $exifTool = shift;
    my $value = $$exifTool{VALUE};
    my ($i, $track);
    # get the video track family 1 group (ie. "Track1");
    for ($i=0; ; ++$i) {
        my $idx = $i ? " ($i)" : '';
        my $tag = "HandlerType$idx";
        last unless $$value{$tag};
        next unless $$value{$tag} eq 'vide';
        $track = $exifTool-&gt;GetGroup($tag, 1);
        last;
    }
    return undef unless $track;
    # get the video track matrix
    for ($i=0; ; ++$i) {
        my $idx = $i ? " ($i)" : '';
        my $tag = "MatrixStructure$idx";
        last unless $$value{$tag};
        next unless $exifTool-&gt;GetGroup($tag, 1) eq $track;
        my @a = split ' ', $$value{$tag};
        return undef unless $a[0] or $a[1];
        # calculate the rotation angle (assume uniform rotation)
        my $angle = atan2($a[1], $a[0]) * 180 / 3.14159;
        $angle += 360 if $angle &lt; 0;
        return int($angle * 1000 + 0.5) / 1000;
    }
    return undef;
}

#------------------------------------------------------------------------------
# Determine the average sample rate from a time-to-sample table
# Inputs: 0) ExifTool object ref, 1) time-to-sample table data ref
# Returns: average sample rate (in Hz)
sub CalcSampleRate($$)
{
    my ($exifTool, $valPt) = @_;
    my @dat = unpack('N*', $$valPt);
    my ($num, $dur) = (0, 0);
    my $i;
    for ($i=2; $i&lt;@dat-1; $i+=2) {
        $num += $dat[$i];               # total number of samples
        $dur += $dat[$i] * $dat[$i+1];  # total sample duration
    }
    return undef unless $num and $dur and $$exifTool{MediaTS};
    return $num * $$exifTool{MediaTS} / $dur;
}

#------------------------------------------------------------------------------
# Fix incorrect format for ImageWidth/Height as written by Pentax
sub FixWrongFormat($)
{
    my $val = shift;
    return undef unless $val;
    if ($val &amp; 0xffff0000) {
        $val = unpack('n',pack('N',$val));
    }
    return $val;
}

#------------------------------------------------------------------------------
# Convert ISO 6709 string to standard lag/lon format
# Inputs: 0) ISO 6709 string (lat, lon, and optional alt)
# Returns: position in decimal degress with altitude if available
# Notes: Wikipedia indicates altitude may be in feet -- how is this specified?
sub ConvertISO6709($)
{
    my $val = shift;
    if ($val =~ /^([-+]\d{2}(?:\.\d*)?)([-+]\d{3}(?:\.\d*)?)([-+]\d+)?/) {
        $val = ($1 + 0) . ' ' . ($2 + 0);
        $val .= ' ' . ($3 + 0) if $3;
    } elsif ($val =~ /^([-+])(\d{2})(\d{2}(?:\.\d*)?)([-+])(\d{3})(\d{2}(?:\.\d*)?)([-+]\d+)?/) {
        my $lat = $2 + $3 / 60;
        $lat = -$lat if $1 eq '-';
        my $lon = $5 + $6 / 60;
        $lon = -$lon if $4 eq '-';
        $val = "$lat $lon";
        $val .= ' ' . ($7 + 0) if $7;
    } elsif ($val =~ /^([-+])(\d{2})(\d{2})(\d{2}(?:\.\d*)?)([-+])(\d{3})(\d{2})(\d{2}(?:\.\d*)?)([-+]\d+)?/) {
        my $lat = $2 + $3 / 60 + $4 / 3600;
        $lat = -$lat if $1 eq '-';
        my $lon = $6 + $7 / 60 + $8 / 3600;
        $lon = -$lon if $5 eq '-';
        $val = "$lat $lon";
        $val .= ' ' . ($9 + 0) if $9;
    }
    return $val;
}

#------------------------------------------------------------------------------
# Format GPSCoordinates for printing
# Inputs: 0) string with numerical lat, lon and optional alt, separated by spaces
#         1) ExifTool object reference
# Returns: PrintConv value
sub PrintGPSCoordinates($)
{
    my ($val, $exifTool) = @_;
    require Image::ExifTool::GPS;
    my @v = split ' ', $val;
    my $prt = Image::ExifTool::GPS::ToDMS($exifTool, $v[0], 1, "N") . ', ' .
              Image::ExifTool::GPS::ToDMS($exifTool, $v[1], 1, "E");
    if (defined $v[2]) {
        $prt .= ', ' . ($v[2] &lt; 0 ? -$v[2] . ' m Below' : $v[2] . ' m Above') . ' Sea Level';
    }
    return $prt;
}

#------------------------------------------------------------------------------
# Unpack packed ISO 639/T language code
# Inputs: 0) packed language code (or undef)
# Returns: language code, or undef for default language, or 'err' for format error
sub UnpackLang($)
{
    my $lang = shift;
    if ($lang) {
        # language code is packed in 5-bit characters
        $lang = pack "C*", map { (($lang&gt;&gt;$_)&amp;0x1f)+0x60 } 10, 5, 0;
        # validate language code
        if ($lang =~ /^[a-z]+$/) {
            # treat 'eng' or 'und' as the default language
            undef $lang if $lang eq 'und' or $lang eq 'eng';
        } else {
            $lang = 'err';  # invalid language code
        }
    }
    return $lang;
}

#------------------------------------------------------------------------------
# Process MPEG-4 MTDT atom (ref 11)
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
# Returns: 1 on success
sub ProcessMetaData($$$)
{
    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
    my $dataPt = $$dirInfo{DataPt};
    my $dirLen = length $$dataPt;
    my $verbose = $exifTool-&gt;Options('Verbose');
    return 0 unless $dirLen &gt;= 2;
    my $count = Get16u($dataPt, 0);
    $verbose and $exifTool-&gt;VerboseDir('MetaData', $count);
    my $i;
    my $pos = 2;
    for ($i=0; $i&lt;$count; ++$i) {
        last if $pos + 10 &gt; $dirLen;
        my $size = Get16u($dataPt, $pos);
        last if $size &lt; 10 or $size + $pos &gt; $dirLen;
        my $tag  = Get32u($dataPt, $pos + 2);
        my $lang = Get16u($dataPt, $pos + 6);
        my $enc  = Get16u($dataPt, $pos + 8);
        my $val  = substr($$dataPt, $pos + 10, $size);
        my $tagInfo = $exifTool-&gt;GetTagInfo($tagTablePtr, $tag);
        if ($tagInfo) {
            # convert language code to ASCII (ignore read-only bit)
            $lang = UnpackLang($lang);
            # handle alternate languages
            if ($lang) {
                my $langInfo = Image::ExifTool::GetLangInfo($tagInfo, $lang);
                $tagInfo = $langInfo if $langInfo;
            }
            $verbose and $exifTool-&gt;VerboseInfo($tag, $tagInfo,
                Value  =&gt; $val,
                DataPt =&gt; $dataPt,
                Start  =&gt; $pos + 10,
                Size   =&gt; $size - 10,
            );
            # convert from UTF-16 BE if necessary
            $val = $exifTool-&gt;Decode($val, 'UCS2') if $enc == 1;
            if ($enc == 0 and $$tagInfo{Unknown}) {
                # binary data
                $exifTool-&gt;FoundTag($tagInfo, \$val);
            } else {
                $exifTool-&gt;FoundTag($tagInfo, $val);
            }
        }
        $pos += $size;
    }
    return 1;
}

#------------------------------------------------------------------------------
# Process Meta keys and add tags to the ItemList table ('mdta' handler) (ref PH)
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
# Returns: 1 on success
sub ProcessKeys($$$)
{
    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
    my $dataPt = $$dirInfo{DataPt};
    my $dirLen = length $$dataPt;
    my $out;
    if ($exifTool-&gt;Options('Verbose')) {
        $exifTool-&gt;VerboseDir('Keys');
        $out = $exifTool-&gt;Options('TextOut');
    }
    my $pos = 8;
    my $index = 1;
    my $infoTable = GetTagTable('Image::ExifTool::QuickTime::ItemList');
    my $userTable = GetTagTable('Image::ExifTool::QuickTime::UserData');
    while ($pos &lt; $dirLen - 4) {
        my $len = unpack("x${pos}N", $$dataPt);
        last if $len &lt; 8 or $pos + $len &gt; $dirLen;
        delete $$tagTablePtr{$index};
        my $ns  = substr($$dataPt, $pos + 4, 4);
        my $tag = substr($$dataPt, $pos + 8, $len - 8);
        $tag =~ s/\0.*//s; # truncate at null
        if ($ns eq 'mdta') {
            $tag =~ s/^com\.apple\.quicktime\.//;   # remove common apple quicktime domain
        }
        next unless $tag;
        # (I have some samples where the tag is a reversed ItemList or UserData tag ID)
        my $tagInfo = $exifTool-&gt;GetTagInfo($tagTablePtr, $tag);
        unless ($tagInfo) {
            $tagInfo = $exifTool-&gt;GetTagInfo($infoTable, $tag);
            unless ($tagInfo) {
                $tagInfo = $exifTool-&gt;GetTagInfo($userTable, $tag);
                if (not $tagInfo and $tag =~ /^\w{3}\xa9$/) {
                    $tag = pack('N', unpack('V', $tag));
                    $tagInfo = $exifTool-&gt;GetTagInfo($infoTable, $tag);
                    $tagInfo or $tagInfo = $exifTool-&gt;GetTagInfo($userTable, $tag);
                }
            }
        }
        my $newInfo;
        if ($tagInfo) {
            $newInfo = {
                Name      =&gt; $$tagInfo{Name},
                Format    =&gt; $$tagInfo{Format},
                ValueConv =&gt; $$tagInfo{ValueConv},
                PrintConv =&gt; $$tagInfo{PrintConv},
            };
            my $groups = $$tagInfo{Groups};
            $$newInfo{Groups} = { %$groups } if $groups;
        } elsif ($tag =~ /^[-\w.]+$/) {
            # create info for tags with reasonable id's
            my $name = $tag;
            $name =~ s/\.(.)/\U$1/g;
            $newInfo = { Name =&gt; ucfirst($name) };
        }
        # substitute this tag in the ItemList table with the given index
        delete $$infoTable{$index};
        if ($newInfo) {
            Image::ExifTool::AddTagToTable($infoTable, $index, $newInfo);
            $out and printf $out "%sAdded ItemList Tag 0x%.4x = $tag\n", $exifTool-&gt;{INDENT}, $index;
        }
        $pos += $len;
        ++$index;
    }
    return 1;
}

#------------------------------------------------------------------------------
# Process a QuickTime atom
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) optional tag table ref
# Returns: 1 on success
sub ProcessMOV($$;$)
{
    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
    my $raf = $$dirInfo{RAF};
    my $dataPt = $$dirInfo{DataPt};
    my $verbose = $exifTool-&gt;Options('Verbose');
    my $dataPos = $$dirInfo{Base} || 0;
    my ($buff, $tag, $size, $track);

    # more convenient to package data as a RandomAccess file
    $raf or $raf = new File::RandomAccess($dataPt);
    # skip leading bytes if necessary
    if ($$dirInfo{DirStart}) {
        $raf-&gt;Seek($$dirInfo{DirStart}, 1) or return 0;
        $dataPos += $$dirInfo{DirStart};
    }
    # read size/tag name atom header
    $raf-&gt;Read($buff,8) == 8 or return 0;
    $dataPos += 8;
    $tagTablePtr or $tagTablePtr = GetTagTable('Image::ExifTool::QuickTime::Main');
    ($size, $tag) = unpack('Na4', $buff);
    if ($dataPt) {
        $verbose and $exifTool-&gt;VerboseDir($$dirInfo{DirName});
    } else {
        # check on file type if called with a RAF
        $$tagTablePtr{$tag} or return 0;
        if ($tag eq 'ftyp') {
            # read ahead 4 bytes to see what type of file this is
            my $fileType;
            if ($raf-&gt;Read($buff, 4) == 4) {
                $raf-&gt;Seek(-4, 1);
                # see if we know the extension for this file type
                $fileType = $1 if $ftypLookup{$buff} and $ftypLookup{$buff} =~ /\(\.(\w+)/;
            }
            $fileType or $fileType = 'MP4'; # default to MP4
            $exifTool-&gt;SetFileType($fileType, $mimeLookup{$fileType} || 'video/mp4');
        } else {
            $exifTool-&gt;SetFileType();       # MOV
        }
        SetByteOrder('MM');
    }
    for (;;) {
        if ($size &lt; 8) {
            last if $size == 0;
            $size == 1 or $exifTool-&gt;Warn('Invalid atom size'), last;
            $raf-&gt;Read($buff, 8) == 8 or last;
            $dataPos += 8;
            my ($hi, $lo) = unpack('NN', $buff);
            $size = $lo;
            if ($hi or $lo &gt; 0x7fffffff) {
                if ($hi &gt; 0x7fffffff) {
                    $exifTool-&gt;Warn('Invalid atom size');
                    last;
                } elsif (not $exifTool-&gt;Options('LargeFileSupport')) {
                    $exifTool-&gt;Warn('End of processing at large atom (LargeFileSupport not enabled)');
                    last;
                }
            }
            $size = $hi * 4294967296 + $lo;
        }
        $size -= 8;
        my $tagInfo = $exifTool-&gt;GetTagInfo($tagTablePtr, $tag);
        # allow numerical tag ID's
        unless ($tagInfo) {
            my $num = unpack('N', $tag);
            if ($$tagTablePtr{$num}) {
                $tagInfo = $exifTool-&gt;GetTagInfo($tagTablePtr, $num);
                $tag = $num;
            }
        }
        # generate tagInfo if Unknown option set
        if (not defined $tagInfo and ($exifTool-&gt;{OPTIONS}-&gt;{Unknown} or
            $verbose or $tag =~ /^\xa9/))
        {
            my $name = $tag;
            my $n = ($name =~ s/([\x00-\x1f\x7f-\xff])/'x'.unpack('H*',$1)/eg);
            # print in hex if tag is numerical
            $name = sprintf('0x%.4x',unpack('N',$tag)) if $n &gt; 2;
            if ($name =~ /^xa9(.*)/) {
                $tagInfo = {
                    Name =&gt; "UserData_$1",
                    Description =&gt; "User Data $1",
                };
            } else {
                $tagInfo = {
                    Name =&gt; "Unknown_$name",
                    Description =&gt; "Unknown $name",
                    Unknown =&gt; 1,
                    Binary =&gt; 1,
                };
            }
            Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo);
        }
        # save required tag sizes
        $exifTool-&gt;HandleTag($tagTablePtr, "$tag-size", $size) if $$tagTablePtr{"$tag-size"};
        # load values only if associated with a tag (or verbose) and &lt; 16MB long
        if ((defined $tagInfo or $verbose) and $size &lt; 0x1000000) {
            my $val;
            unless ($raf-&gt;Read($val, $size) == $size) {
                $exifTool-&gt;Warn("Truncated '$tag' data");
                last;
            }
            # use value to get tag info if necessary
            $tagInfo or $tagInfo = $exifTool-&gt;GetTagInfo($tagTablePtr, $tag, \$val);
            my $hasData = ($$dirInfo{HasData} and $val =~ /\0...data\0/s);
            if ($verbose and not $hasData) {
                $exifTool-&gt;VerboseInfo($tag, $tagInfo,
                    Value =&gt; $val,
                    DataPt =&gt; \$val,
                    DataPos =&gt; $dataPos,
                );
            }
            if ($tagInfo) {
                my $subdir = $$tagInfo{SubDirectory};
                if ($subdir) {
                    my $start = $$subdir{Start} || 0;
                    my ($base, $dPos) = ($dataPos, 0);
                    if ($$subdir{Base}) {
                        $dPos -= eval $$subdir{Base};
                        $base -= $dPos;
                    }
                    my %dirInfo = (
                        DataPt   =&gt; \$val,
                        DataLen  =&gt; $size,
                        DirStart =&gt; $start,
                        DirLen   =&gt; $size - $start,
                        DirName  =&gt; $$subdir{DirName} || $$tagInfo{Name},
                        HasData  =&gt; $$subdir{HasData},
                        Multi    =&gt; $$subdir{Multi},
                        DataPos  =&gt; $dPos,
                        # Base needed for IsOffset tags in binary data
                        Base     =&gt; $base,
                    );
                    if ($$subdir{ByteOrder} and $$subdir{ByteOrder} =~ /^Little/) {
                        SetByteOrder('II');
                    }
                    my $oldGroup1 = $exifTool-&gt;{SET_GROUP1};
                    if ($$tagInfo{Name} eq 'Track') {
                        $track or $track = 0;
                        $exifTool-&gt;{SET_GROUP1} = 'Track' . (++$track);
                    }
                    my $subTable = GetTagTable($$subdir{TagTable});
                    my $proc = $$subdir{ProcessProc};
                    $exifTool-&gt;ProcessDirectory(\%dirInfo, $subTable, $proc) if $size &gt; $start;
                    $exifTool-&gt;{SET_GROUP1} = $oldGroup1;
                    SetByteOrder('MM');
                } elsif ($hasData) {
                    # handle atoms containing 'data' tags
                    # (currently ignore contained atoms: 'itif', 'name', etc.)
                    my $pos = 0;
                    for (;;) {
                        last if $pos + 16 &gt; $size;
                        my ($len, $type, $flags, $ctry, $lang) = unpack("x${pos}Na4Nnn", $val);
                        last if $pos + $len &gt; $size;
                        my $value;
                        my $format = $$tagInfo{Format};
                        if ($type eq 'data' and $len &gt;= 16) {
                            $pos += 16;
                            $len -= 16;
                            $value = substr($val, $pos, $len);
                            # format flags (ref 12):
                            # 0x0=binary, 0x1=UTF-8, 0x2=UTF-16, 0x3=ShiftJIS,
                            # 0x4=UTF-8  0x5=UTF-16, 0xd=JPEG, 0xe=PNG,
                            # 0x15=signed int, 0x16=unsigned int, 0x17=float,
                            # 0x18=double, 0x1b=BMP, 0x1c='meta' atom
                            if ($stringEncoding{$flags}) {
                                # handle all string formats
                                $value = $exifTool-&gt;Decode($value, $stringEncoding{$flags});
                            } else {
                                if (not $format) {
                                    if ($flags == 0x15 or $flags == 0x16) {
                                        $format = { 1=&gt;'int8', 2=&gt;'int16', 4=&gt;'int32' }-&gt;{$len};
                                        $format .= $flags == 0x15 ? 's' : 'u' if $format;
                                    } elsif ($flags == 0x17) {
                                        $format = 'float';
                                    } elsif ($flags == 0x18) {
                                        $format = 'double';
                                    } elsif ($flags == 0x00) {
                                        # read 1 and 2-byte binary as integers
                                        if ($len == 1) {
                                            $format = 'int8u',
                                        } elsif ($len == 2) {
                                            $format = 'int16u',
                                        }
                                    }
                                }
                                if ($format) {
                                    $value = ReadValue(\$value, 0, $format, $$tagInfo{Count}, $len);
                                } elsif (not $$tagInfo{ValueConv}) {
                                    # make binary data a scalar reference unless a ValueConv exists
                                    my $buf = $value;
                                    $value = \$buf;
                                }
                            }
                        }
                        my $langInfo;
                        if ($ctry or $lang) {
                            # ignore country ('ctry') and language lists ('lang') for now
                            undef $ctry if $ctry and $ctry &lt;= 255;
                            undef $lang if $lang and $lang &lt;= 255;
                            $lang = UnpackLang($lang);
                            # add country code if specified
                            if ($ctry) {
                                $ctry = unpack('a2',pack('n',$ctry)); # unpack as ISO 3166-1
                                # treat 'ZZ' like a default country (see ref 12)
                                undef $ctry if $ctry eq 'ZZ';
                                if ($ctry and $ctry =~ /^[A-Z]{2}$/) {
                                    $lang or $lang = 'und';
                                    $lang .= "-$ctry";
                                }
                            }
                            $langInfo = Image::ExifTool::GetLangInfo($tagInfo, $lang) if $lang;
                        }
                        $langInfo or $langInfo = $tagInfo;
                        $exifTool-&gt;VerboseInfo($tag, $langInfo,
                            Value   =&gt; ref $value ? $$value : $value,
                            DataPt  =&gt; \$val,
                            DataPos =&gt; $dataPos,
                            Start   =&gt; $pos,
                            Size    =&gt; $len,
                            Format  =&gt; $format,
                            Extra   =&gt; sprintf(", Type='$type', Flags=0x%x",$flags)
                        ) if $verbose;
                        $exifTool-&gt;FoundTag($langInfo, $value) if defined $value;
                        $pos += $len;
                    }
                } elsif ($tag =~ /^\xa9/) {
                    # parse international text to extract all languages
                    my $pos = 0;
                    for (;;) {
                        last if $pos + 4 &gt; $size;
                        my ($len, $lang) = unpack("x${pos}nn", $val);
                        $pos += 4;
                        # according to the QuickTime spec (ref 12), $len should include
                        # 4 bytes for length and type words, but nobody (including
                        # Apple, Pentax and Kodak) seems to add these in, so try
                        # to allow for either
                        if ($pos + $len &gt; $size) {
                            $len -= 4;
                            last if $pos + $len &gt; $size or $len &lt; 0;
                        }
                        # ignore any empty entries (or null padding) after the first
                        next if not $len and $pos;
                        my $str = substr($val, $pos, $len);
                        my $langInfo;
                        if ($lang &lt; 0x400) {
                            # this is a Macintosh language code
                            # a language code of 0 is Macintosh english, so treat as default
                            if ($lang) { 
                                # use Font.pm to look up language string
                                require Image::ExifTool::Font;
                                $lang = $Image::ExifTool::Font::ttLang{Macintosh}{$lang};
                            }
                            # the spec says only "Macintosh text encoding", so
                            # I can only assume that it is the most common one
                            $str = $exifTool-&gt;Decode($str, 'MacRoman');
                        } else {
                            # convert language code to ASCII (ignore read-only bit)
                            $lang = UnpackLang($lang);
                            # may be either UTF-8 or UTF-16BE
                            my $enc = $str=~s/^\xfe\xff// ? 'UTF16' : 'UTF8';
                            $str = $exifTool-&gt;Decode($str, $enc);
                        }
                        $langInfo = Image::ExifTool::GetLangInfo($tagInfo, $lang) if $lang;
                        $exifTool-&gt;FoundTag($langInfo || $tagInfo, $str);
                        $pos += $len;
                    }
                } else {
                    if ($$tagInfo{Format}) {
                        $val = ReadValue(\$val, 0, $$tagInfo{Format}, $$tagInfo{Count}, length($val));
                    }
                    $exifTool-&gt;FoundTag($tagInfo, $val);
                }
            }
        } else {
            $raf-&gt;Seek($size, 1) or $exifTool-&gt;Warn("Truncated '$tag' data"), last;
        }
        $raf-&gt;Read($buff, 8) == 8 or last;
        $dataPos += $size + 8;
        ($size, $tag) = unpack('Na4', $buff);
    }
    return 1;
}

#------------------------------------------------------------------------------
# Process a QuickTime Image File
# Inputs: 0) ExifTool object reference, 1) directory information reference
# Returns: 1 on success
sub ProcessQTIF($$)
{
    my ($exifTool, $dirInfo) = @_;
    my $table = GetTagTable('Image::ExifTool::QuickTime::ImageFile');
    return ProcessMOV($exifTool, $dirInfo, $table);
}

1;  # end

__END__

=head1 NAME

Image::ExifTool::QuickTime - Read QuickTime and MP4 meta information

=head1 SYNOPSIS

This module is used by Image::ExifTool

=head1 DESCRIPTION

This module contains routines required by Image::ExifTool to extract
information from QuickTime and MP4 video, and M4A audio files.

=head1 AUTHOR

Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca)

This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=head1 REFERENCES

=over 4

=item L&lt;http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html&gt;

=item L&lt;http://search.cpan.org/dist/MP4-Info-1.04/&gt;

=item L&lt;http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt&gt;

=item L&lt;http://wiki.multimedia.cx/index.php?title=Apple_QuickTime&gt;

=item L&lt;http://atomicparsley.sourceforge.net/mpeg-4files.html&gt;

=item L&lt;http://wiki.multimedia.cx/index.php?title=QuickTime_container&gt;

=item L&lt;http://code.google.com/p/mp4v2/wiki/iTunesMetadata&gt;

=item L&lt;http://www.canieti.com.mx/assets/files/1011/IEC_100_1384_DC.pdf&gt;

=item L&lt;http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf&gt;

=back

=head1 SEE ALSO

L&lt;Image::ExifTool::TagNames/QuickTime Tags&gt;,
L&lt;Image::ExifTool(3pm)|Image::ExifTool&gt;

=cut

</pre></body></html>