exiftool_vendored 12.22.0 → 12.25.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +50 -0
  3. data/bin/MANIFEST +11 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +44 -43
  7. data/bin/config_files/acdsee.config +193 -6
  8. data/bin/config_files/cuepointlist.config +70 -0
  9. data/bin/exiftool +45 -44
  10. data/bin/lib/Image/ExifTool.pm +85 -26
  11. data/bin/lib/Image/ExifTool.pod +61 -50
  12. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +1 -1
  13. data/bin/lib/Image/ExifTool/Canon.pm +3 -2
  14. data/bin/lib/Image/ExifTool/DjVu.pm +6 -5
  15. data/bin/lib/Image/ExifTool/Exif.pm +16 -7
  16. data/bin/lib/Image/ExifTool/FujiFilm.pm +18 -8
  17. data/bin/lib/Image/ExifTool/JPEG.pm +6 -2
  18. data/bin/lib/Image/ExifTool/JSON.pm +24 -3
  19. data/bin/lib/Image/ExifTool/Jpeg2000.pm +361 -16
  20. data/bin/lib/Image/ExifTool/MRC.pm +341 -0
  21. data/bin/lib/Image/ExifTool/MWG.pm +3 -3
  22. data/bin/lib/Image/ExifTool/MXF.pm +1 -1
  23. data/bin/lib/Image/ExifTool/MacOS.pm +1 -1
  24. data/bin/lib/Image/ExifTool/Microsoft.pm +5 -3
  25. data/bin/lib/Image/ExifTool/Nikon.pm +3 -2
  26. data/bin/lib/Image/ExifTool/NikonSettings.pm +10 -1
  27. data/bin/lib/Image/ExifTool/PNG.pm +2 -2
  28. data/bin/lib/Image/ExifTool/Panasonic.pm +14 -1
  29. data/bin/lib/Image/ExifTool/PhaseOne.pm +4 -3
  30. data/bin/lib/Image/ExifTool/QuickTime.pm +102 -44
  31. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +88 -29
  32. data/bin/lib/Image/ExifTool/RIFF.pm +83 -11
  33. data/bin/lib/Image/ExifTool/Samsung.pm +2 -1
  34. data/bin/lib/Image/ExifTool/Sony.pm +98 -24
  35. data/bin/lib/Image/ExifTool/TagLookup.pm +56 -15
  36. data/bin/lib/Image/ExifTool/TagNames.pod +388 -173
  37. data/bin/lib/Image/ExifTool/WritePostScript.pl +1 -0
  38. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +37 -12
  39. data/bin/lib/Image/ExifTool/WriteXMP.pl +6 -2
  40. data/bin/lib/Image/ExifTool/Writer.pl +1 -0
  41. data/bin/lib/Image/ExifTool/XMP.pm +20 -7
  42. data/bin/perl-Image-ExifTool.spec +42 -42
  43. data/lib/exiftool_vendored/version.rb +1 -1
  44. metadata +6 -8
@@ -17,7 +17,7 @@ use strict;
17
17
  use vars qw($VERSION);
18
18
  use Image::ExifTool qw(:DataAccess :Utils);
19
19
 
20
- $VERSION = '1.02';
20
+ $VERSION = '1.03';
21
21
 
22
22
  sub ProcessNikonSettings($$$);
23
23
 
@@ -1919,6 +1919,15 @@ my %infoD6 = (
1919
1919
  10 => 'Auto (Animals)',
1920
1920
  },
1921
1921
  },
1922
+ 0x170 => { Name => 'PreferSubSelectorCenter', PrintConv => \%offOn }, # CSf13 (D6 firmware v1.2.0)
1923
+ 0x174 => { # CSa17-d (D6 firmware v1.2.0)
1924
+ Name => 'FocusPointSelectionSpeed',
1925
+ PrintConv => {
1926
+ 1 => 'Normal',
1927
+ 2 => 'High',
1928
+ 3 => 'Very High',
1929
+ },
1930
+ },
1922
1931
  );
1923
1932
 
1924
1933
  #------------------------------------------------------------------------------
@@ -36,7 +36,7 @@ use strict;
36
36
  use vars qw($VERSION $AUTOLOAD %stdCase);
37
37
  use Image::ExifTool qw(:DataAccess :Utils);
38
38
 
39
- $VERSION = '1.56';
39
+ $VERSION = '1.57';
40
40
 
41
41
  sub ProcessPNG_tEXt($$$);
42
42
  sub ProcessPNG_iTXt($$$);
@@ -975,7 +975,7 @@ sub FoundPNG($$$$;$$$$)
975
975
  $$tagInfo{LangCode} = $lang if $lang;
976
976
  # make unknown profiles binary data type
977
977
  $$tagInfo{Binary} = 1 if $tag =~ /^Raw profile type /;
978
- $verbose and $et->VPrint(0, " | [adding $tag]\n");
978
+ $verbose and $et->VPrint(0, " [adding $tag]\n");
979
979
  AddTagToTable($tagTablePtr, $tag, $tagInfo);
980
980
  }
981
981
  #
@@ -2118,6 +2118,7 @@ my %shootingMode = (
2118
2118
  Name => 'UserProfile',
2119
2119
  Writable => 'string',
2120
2120
  },
2121
+ # 0x357 int32u - 0=DNG, 3162=JPG (ref 23)
2121
2122
  0x359 => { #23
2122
2123
  Name => 'ISOSelected',
2123
2124
  Writable => 'int32s',
@@ -2134,7 +2135,19 @@ my %shootingMode = (
2134
2135
  PrintConv => 'sprintf("%.1f", $val)',
2135
2136
  PrintConvInv => '$val',
2136
2137
  },
2137
- # 0x357 int32u - 0=DNG, 3162=JPG (ref 23)
2138
+ 0x035b => { #IB
2139
+ Name => 'CorrelatedColorTemp', # (in Kelvin)
2140
+ Writable => 'int16u',
2141
+ },
2142
+ 0x035c => { #IB
2143
+ Name => 'ColorTint', # (same units as Adobe is using)
2144
+ Writable => 'int16s',
2145
+ },
2146
+ 0x035d => { #IB
2147
+ Name => 'WhitePoint', # (x/y)
2148
+ Writable => 'rational64u',
2149
+ Count => 2,
2150
+ },
2138
2151
  );
2139
2152
 
2140
2153
  # Type 2 tags (ref PH)
@@ -15,7 +15,7 @@ use vars qw($VERSION);
15
15
  use Image::ExifTool qw(:DataAccess :Utils);
16
16
  use Image::ExifTool::Exif;
17
17
 
18
- $VERSION = '1.06';
18
+ $VERSION = '1.07';
19
19
 
20
20
  sub WritePhaseOne($$$);
21
21
  sub ProcessPhaseOne($$$);
@@ -73,10 +73,11 @@ my @formatName = ( undef, 'string', 'int16s', undef, 'int32s' );
73
73
  PrintConv => { #PH
74
74
  1 => 'RAW 1', #? (encrypted)
75
75
  2 => 'RAW 2', #? (encrypted)
76
- 3 => 'IIQ L',
76
+ 3 => 'IIQ L', # (now "L14", ref IB)
77
77
  # 4?
78
78
  5 => 'IIQ S',
79
- 6 => 'IIQ Sv2',
79
+ 6 => 'IIQ Sv2', # (now "S14" for "IIQ 14 Smart" and "IIQ 14 Sensor+", ref IB)
80
+ 8 => 'IIQ L16', #IB ("IIQ 16 Extended" and "IIQ 16 Large")
80
81
  },
81
82
  },
82
83
  0x010f => {
@@ -47,7 +47,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
47
47
  use Image::ExifTool::Exif;
48
48
  use Image::ExifTool::GPS;
49
49
 
50
- $VERSION = '2.61';
50
+ $VERSION = '2.63';
51
51
 
52
52
  sub ProcessMOV($$;$);
53
53
  sub ProcessKeys($$$);
@@ -65,6 +65,7 @@ sub Process_gsen($$$);
65
65
  sub ProcessRIFFTrailer($$$);
66
66
  sub ProcessTTAD($$$);
67
67
  sub ProcessNMEA($$$);
68
+ sub ProcessGPSLog($$$);
68
69
  sub SaveMetaKeys($$$);
69
70
  # ++^^^^^^^^^^^^++
70
71
  sub ParseItemLocation($$);
@@ -160,8 +161,15 @@ my %ftypLookup = (
160
161
  'F4P ' => 'Protected Video for Adobe Flash Player 9+ (.F4P)', # video/mp4
161
162
  'F4V ' => 'Video for Adobe Flash Player 9+ (.F4V)', # video/mp4
162
163
  'isc2' => 'ISMACryp 2.0 Encrypted File', # ?/enc-isoff-generic
163
- 'iso2' => 'MP4 Base Media v2 [ISO 14496-12:2005]', # video/mp4
164
- 'isom' => 'MP4 Base Media v1 [IS0 14496-12:2003]', # video/mp4
164
+ 'iso2' => 'MP4 Base Media v2 [ISO 14496-12:2005]', # video/mp4 (or audio)
165
+ 'iso3' => 'MP4 Base Media v3', # video/mp4 (or audio)
166
+ 'iso4' => 'MP4 Base Media v4', # video/mp4 (or audio)
167
+ 'iso5' => 'MP4 Base Media v5', # video/mp4 (or audio)
168
+ 'iso6' => 'MP4 Base Media v6', # video/mp4 (or audio)
169
+ 'iso7' => 'MP4 Base Media v7', # video/mp4 (or audio)
170
+ 'iso8' => 'MP4 Base Media v8', # video/mp4 (or audio)
171
+ 'iso9' => 'MP4 Base Media v9', # video/mp4 (or audio)
172
+ 'isom' => 'MP4 Base Media v1 [IS0 14496-12:2003]', # video/mp4 (or audio)
165
173
  'JP2 ' => 'JPEG 2000 Image (.JP2) [ISO 15444-1 ?]', # image/jp2
166
174
  'JP20' => 'Unknown, from GPAC samples (prob non-existent)',
167
175
  'jpm ' => 'JPEG 2000 Compound Image (.JPM) [ISO 15444-6]', # image/jpm
@@ -678,6 +686,15 @@ my %eeBox2 = (
678
686
  udat => { #PH (GPS NMEA-format log written by Datakam Player software)
679
687
  Name => 'GPSLog',
680
688
  Binary => 1, # (actually ASCII, but very lengthy)
689
+ Notes => 'parsed to extract GPS separately when ExtractEmbedded is used',
690
+ RawConv => q{
691
+ $val =~ s/\0+$//; # remove trailing nulls
692
+ if (length $val and $$self{OPTIONS}{ExtractEmbedded}) {
693
+ my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
694
+ Image::ExifTool::QuickTime::ProcessGPSLog($self, { DataPt => \$val }, $tagTbl);
695
+ }
696
+ return $val;
697
+ },
681
698
  },
682
699
  # meta - proprietary XML information written by some Flip cameras - PH
683
700
  # beam - 16 bytes found in an iPhone video
@@ -1564,36 +1581,29 @@ my %eeBox2 = (
1564
1581
  coll => { Name => 'CollectionName', %langText3gp }, #17
1565
1582
  rtng => {
1566
1583
  Name => 'Rating',
1584
+ Writable => 'undef',
1585
+ Avoid => 1,
1567
1586
  # (4-byte flags, 4-char entity, 4-char criteria, 2-byte lang, string)
1568
- RawConv => q{
1569
- return '<err>' unless length $val >= 14;
1570
- my $str = 'Entity=' . substr($val,4,4) . ' Criteria=' . substr($val,8,4);
1571
- $str =~ tr/\0-\x1f\x7f-\xff//d; # remove unprintable characters
1572
- my $lang = Image::ExifTool::QuickTime::UnpackLang(Get16u(\$val, 12));
1573
- $lang = $lang ? "($lang) " : '';
1574
- $val = substr($val, 14);
1575
- $val = $self->Decode($val, 'UCS2') if $val =~ /^\xfe\xff/;
1576
- return $lang . $str . ' ' . $val;
1577
- },
1587
+ IText => 14, # (14 bytes before string)
1588
+ Notes => 'string in the form "Entity=XXXX Criteria=XXXX XXXXX", used in 3gp videos',
1589
+ ValueConv => '$val=~s/^(.{4})(.{4})/Entity=$1 Criteria=$2 /i; $val',
1590
+ ValueConvInv => '$val=~s/Entity=(.{4}) Criteria=(.{4}) ?/$1$2/i; $val',
1578
1591
  },
1579
1592
  clsf => {
1580
1593
  Name => 'Classification',
1594
+ Writable => 'undef',
1595
+ Avoid => 1,
1581
1596
  # (4-byte flags, 4-char entity, 2-byte index, 2-byte lang, string)
1582
- RawConv => q{
1583
- return '<err>' unless length $val >= 12;
1584
- my $str = 'Entity=' . substr($val,4,4) . ' Index=' . Get16u(\$val,8);
1585
- $str =~ tr/\0-\x1f\x7f-\xff//d; # remove unprintable characters
1586
- my $lang = Image::ExifTool::QuickTime::UnpackLang(Get16u(\$val, 10));
1587
- $lang = $lang ? "($lang) " : '';
1588
- $val = substr($val, 12);
1589
- $val = $self->Decode($val, 'UCS2') if $val =~ /^\xfe\xff/;
1590
- return $lang . $str . ' ' . $val;
1591
- },
1597
+ IText => 12,
1598
+ Notes => 'string in the form "Entity=XXXX Index=### XXXXX", used in 3gp videos',
1599
+ ValueConv => '$val=~s/^(.{4})(.{2})/"Entity=$1 Index=".unpack("n",$2)." "/ie; $val',
1600
+ ValueConvInv => '$val=~s/Entity=(.{4}) Index=(\d+) ?/$1.pack("n",$2)/ie; $val',
1592
1601
  },
1593
1602
  kywd => {
1594
1603
  Name => 'Keywords',
1595
1604
  # (4 byte flags, 2-byte lang, 1-byte count, count x pascal strings, ref 17)
1596
- # (but I have also seen a simple string written by iPhone)
1605
+ # (but I have also seen a simple string written by iPhone, so don't make writable yet)
1606
+ Notes => "not writable because Apple doesn't follow the 3gp specification",
1597
1607
  RawConv => q{
1598
1608
  my $sep = $self->Options('ListSep');
1599
1609
  return join($sep, split /\0+/, $val) unless $val =~ /^\0/; # (iPhone)
@@ -1618,20 +1628,24 @@ my %eeBox2 = (
1618
1628
  loci => {
1619
1629
  Name => 'LocationInformation',
1620
1630
  Groups => { 2 => 'Location' },
1631
+ Writable => 'undef',
1632
+ IText => 6,
1633
+ Avoid => 1,
1634
+ NoDecode => 1, # (we'll decode the data ourself)
1635
+ Notes => q{
1636
+ string in the form "XXXXX Role=XXX Lat=XXX Lon=XXX Alt=XXX Body=XXX
1637
+ Notes=XXX", used in 3gp videos
1638
+ },
1621
1639
  # (4-byte flags, 2-byte lang, location string, 1-byte role, 4-byte fixed longitude,
1622
1640
  # 4-byte fixed latitude, 4-byte fixed altitude, body string, notes string)
1623
1641
  RawConv => q{
1624
- return '<err>' unless length $val >= 6;
1625
- my $lang = Image::ExifTool::QuickTime::UnpackLang(Get16u(\$val, 4));
1626
- $lang = $lang ? "($lang) " : '';
1627
- $val = substr($val, 6);
1628
1642
  my $str;
1629
1643
  if ($val =~ /^\xfe\xff/) {
1630
1644
  $val =~ s/^(\xfe\xff(.{2})*?)\0\0//s or return '<err>';
1631
1645
  $str = $self->Decode($1, 'UCS2');
1632
1646
  } else {
1633
1647
  $val =~ s/^(.*?)\0//s or return '<err>';
1634
- $str = $1;
1648
+ $str = $self->Decode($1, 'UTF8');
1635
1649
  }
1636
1650
  $str = '(none)' unless length $str;
1637
1651
  return '<err>' if length $val < 13;
@@ -1646,27 +1660,52 @@ my %eeBox2 = (
1646
1660
  if ($val =~ s/^(\xfe\xff(.{2})*?)\0\0//s) {
1647
1661
  $str .= ' Body=' . $self->Decode($1, 'UCS2');
1648
1662
  } elsif ($val =~ s/^(.*?)\0//s) {
1649
- $str .= " Body=$1";
1663
+ $str .= ' Body=' . $self->Decode($1, 'UTF8');
1650
1664
  }
1651
1665
  if ($val =~ s/^(\xfe\xff(.{2})*?)\0\0//s) {
1652
1666
  $str .= ' Notes=' . $self->Decode($1, 'UCS2');
1653
1667
  } elsif ($val =~ s/^(.*?)\0//s) {
1654
- $str .= " Notes=$1";
1668
+ $str .= ' Notes=' . $self->Decode($1, 'UTF8');
1655
1669
  }
1656
- return $lang . $str;
1670
+ return $str;
1671
+ },
1672
+ RawConvInv => q{
1673
+ my ($role, $lat, $lon, $alt, $body, $note);
1674
+ $lat = $1 if $val =~ s/ Lat=([-+]?[.\d]+)//i;
1675
+ $lon = $1 if $val =~ s/ Lon=([-+]?[.\d]+)//i;
1676
+ $alt = $1 if $val =~ s/ Alt=([-+]?[.\d]+)//i;
1677
+ $note = $val =~ s/ Notes=(.*)//i ? $1 : '';
1678
+ $body = $val =~ s/ Body=(.*)//i ? $1 : '';
1679
+ $role = $val =~ s/ Role=(.*)//i ? $1 : '';
1680
+ $val = '' if $val eq '(none)';
1681
+ $role = {shooting=>0,real=>1,fictional=>2}->{lc $role} || 0;
1682
+ return $self->Encode($val, 'UTF8') . "\0" . Set8u($role) .
1683
+ SetFixed32s(defined $lon ? $lon : 999) .
1684
+ SetFixed32s(defined $lat ? $lat : 999) .
1685
+ SetFixed32s(defined $alt ? $alt : 0) .
1686
+ $self->Encode($body) . "\0" .
1687
+ $self->Encode($note) . "\0";
1657
1688
  },
1658
1689
  },
1659
1690
  yrrc => {
1660
1691
  Name => 'Year',
1692
+ Writable => 'undef',
1661
1693
  Groups => { 2 => 'Time' },
1662
- RawConv => 'length($val) >= 6 ? Get16u(\$val,4) : "<err>"',
1694
+ Avoid => 1,
1695
+ Notes => 'used in 3gp videos',
1696
+ ValueConv => 'length($val) >= 6 ? unpack("x4n",$val) : "<err>"',
1697
+ ValueConvInv => 'pack("Nn",0,$val)',
1663
1698
  },
1664
1699
  urat => { #17
1665
1700
  Name => 'UserRating',
1666
- RawConv => q{
1701
+ Writable => 'undef',
1702
+ Notes => 'used in 3gp videos',
1703
+ Avoid => 1,
1704
+ ValueConv => q{
1667
1705
  return '<err>' unless length $val >= 8;
1668
- return Get8u(\$val, 7);
1706
+ unpack('x7C', $val);
1669
1707
  },
1708
+ ValueConvInv => 'pack("N2",0,$val)',
1670
1709
  },
1671
1710
  # tsel - TrackSelection (ref 17)
1672
1711
  # Apple tags (ref 16[dead] -- see ref 25 instead)
@@ -7611,7 +7650,11 @@ my %eeBox2 = (
7611
7650
  8 => {
7612
7651
  Name => 'HandlerType',
7613
7652
  Format => 'undef[4]',
7614
- RawConv => '$$self{HandlerType} = $val unless $val eq "alis" or $val eq "url "; $val',
7653
+ RawConv => q{
7654
+ $$self{HandlerType} = $val unless $val eq 'alis' or $val eq 'url ';
7655
+ $$self{HasHandler}{$val} = 1; # remember all our handlers
7656
+ return $val;
7657
+ },
7615
7658
  PrintConvColumns => 2,
7616
7659
  PrintConv => {
7617
7660
  alis => 'Alias Data', #PH
@@ -7716,7 +7759,7 @@ my %eeBox2 = (
7716
7759
  $val =~ s/\0+$//; # remove trailing nulls
7717
7760
  if (length $val and $$self{OPTIONS}{ExtractEmbedded}) {
7718
7761
  my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
7719
- Image::ExifTool::QuickTime::ProcessNMEA($self, { DataPt => \$val }, $tagTbl);
7762
+ Image::ExifTool::QuickTime::ProcessGPSLog($self, { DataPt => \$val }, $tagTbl);
7720
7763
  }
7721
7764
  return $val;
7722
7765
  },
@@ -8949,6 +8992,7 @@ sub ProcessMOV($$;$)
8949
8992
  if ($raf->Read($buff, $size-8) == $size-8) {
8950
8993
  $raf->Seek(-($size-8), 1);
8951
8994
  my $type = substr($buff, 0, 4);
8995
+ $$et{save_ftyp} = $type;
8952
8996
  # see if we know the extension for this file type
8953
8997
  if ($ftypLookup{$type} and $ftypLookup{$type} =~ /\(\.(\w+)/) {
8954
8998
  $fileType = $1;
@@ -9372,9 +9416,9 @@ ItemID: foreach $id (keys %$items) {
9372
9416
  }
9373
9417
  for (;;) {
9374
9418
  my ($len, $lang);
9375
- if ($$tagInfo{IText} and $$tagInfo{IText} == 6) {
9376
- last if $pos + 6 > $size;
9377
- $pos += 4;
9419
+ if ($$tagInfo{IText} and $$tagInfo{IText} >= 6) {
9420
+ last if $pos + $$tagInfo{IText} > $size;
9421
+ $pos += $$tagInfo{IText} - 2;
9378
9422
  $lang = unpack("x${pos}n", $val);
9379
9423
  $pos += 2;
9380
9424
  $len = $size - $pos;
@@ -9394,7 +9438,7 @@ ItemID: foreach $id (keys %$items) {
9394
9438
  # ignore any empty entries (or null padding) after the first
9395
9439
  next if not $len and $pos;
9396
9440
  my $str = substr($val, $pos, $len);
9397
- my $langInfo;
9441
+ my ($langInfo, $enc);
9398
9442
  if (($lang < 0x400 or $lang == 0x7fff) and $str !~ /^\xfe\xff/) {
9399
9443
  # this is a Macintosh language code
9400
9444
  # a language code of 0 is Macintosh english, so treat as default
@@ -9411,15 +9455,22 @@ ItemID: foreach $id (keys %$items) {
9411
9455
  }
9412
9456
  # the spec says only "Macintosh text encoding", but
9413
9457
  # allow this to be configured by the user
9414
- $str = $et->Decode($str, $charsetQuickTime);
9458
+ $enc = $charsetQuickTime;
9415
9459
  } else {
9416
9460
  # convert language code to ASCII (ignore read-only bit)
9417
9461
  $lang = UnpackLang($lang);
9418
9462
  # may be either UTF-8 or UTF-16BE
9419
- my $enc = $str=~s/^\xfe\xff// ? 'UTF16' : 'UTF8';
9463
+ $enc = $str=~s/^\xfe\xff// ? 'UTF16' : 'UTF8';
9464
+ }
9465
+ unless ($$tagInfo{NoDecode}) {
9420
9466
  $str = $et->Decode($str, $enc);
9467
+ $str =~ s/\0+$//; # remove any trailing nulls (eg. 3gp tags)
9468
+ }
9469
+ if ($$tagInfo{IText} and $$tagInfo{IText} > 6) {
9470
+ my $n = $$tagInfo{IText} - 6;
9471
+ # add back extra bytes (eg. 'rtng' box)
9472
+ $str = substr($val, $pos-$n-2, $n) . $str;
9421
9473
  }
9422
- $str =~ s/\0+$//; # remove any trailing nulls (eg. 3gp tags)
9423
9474
  $langInfo = GetLangInfoQT($et, $tagInfo, $lang) if $lang;
9424
9475
  $et->FoundTag($langInfo || $tagInfo, $str);
9425
9476
  $pos += $len;
@@ -9469,6 +9520,13 @@ ItemID: foreach $id (keys %$items) {
9469
9520
  ($size, $tag) = unpack('Na4', $buff);
9470
9521
  ++$index if defined $index;
9471
9522
  }
9523
+ # tweak file type based on track content ("iso*" ftyp only)
9524
+ if ($$et{VALUE}{FileType} and $$et{VALUE}{FileType} eq 'MP4' and
9525
+ $$et{save_ftyp} and $$et{HasHandler} and $$et{save_ftyp} =~ /^iso/ and
9526
+ $$et{HasHandler}{soun} and not $$et{HasHandler}{vide})
9527
+ {
9528
+ $et->OverrideFileType('M4A', 'audio/mp4');
9529
+ }
9472
9530
  # fill in missing defaults for alternate language tags
9473
9531
  # (the first language is taken as the default)
9474
9532
  if ($doDefaultLang and $$et{QTLang}) {
@@ -140,6 +140,7 @@ my %insvLimit = (
140
140
  SampleTime => { Groups => { 2 => 'Video' }, PrintConv => 'ConvertDuration($val)', Notes => 'sample decoding time' },
141
141
  SampleDuration=>{ Groups => { 2 => 'Video' }, PrintConv => 'ConvertDuration($val)' },
142
142
  UserLabel => { Groups => { 2 => 'Other' } },
143
+ KiloCalories => { Groups => { 2 => 'Other' } },
143
144
  SampleDateTime => {
144
145
  Groups => { 2 => 'Time' },
145
146
  ValueConv => q{
@@ -2273,39 +2274,97 @@ sub ProcessNMEA($$$)
2273
2274
  {
2274
2275
  my ($et, $dirInfo, $tagTbl) = @_;
2275
2276
  my $dataPt = $$dirInfo{DataPt};
2276
- # parse only RMC sentence (with leading timestamp) for now
2277
- while ($$dataPt =~ /(?:\[(\d+)\])?\$[A-Z]{2}RMC,(\d{2})(\d{2})(\d+(\.\d*)?),A?,(\d+\.\d+),([NS]),(\d+\.\d+),([EW]),(\d*\.?\d*),(\d*\.?\d*),(\d{2})(\d{2})(\d+)/g) {
2278
- my $tc = $1; # milliseconds since 1970 (local time)
2279
- my ($lat,$latRef,$lon,$lonRef) = ($6,$7,$8,$9);
2280
- my $yr = $14 + ($14 >= 70 ? 1900 : 2000);
2281
- my ($mon,$day,$hr,$min,$sec) = ($13,$12,$2,$3,$4);
2282
- my ($spd, $trk);
2283
- $spd = $10 * $knotsToKph if length $10;
2284
- $trk = $11 if length $11;
2285
- # lat/long are in DDDMM.MMMM format
2286
- my $deg = int($lat / 100);
2287
- $lat = $deg + ($lat - $deg * 100) / 60;
2288
- $deg = int($lon / 100);
2289
- $lon = $deg + ($lon - $deg * 100) / 60;
2290
- $sec = '0' . $sec unless $sec =~ /^\d{2}/; # pad integer part of seconds to 2 digits
2291
- my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%sZ',$yr,$mon,$day,$hr,$min,$sec);
2292
- my $sampleTime;
2293
- $sampleTime = ($tc - $$et{StartTime}) / 1000 if $tc and $$et{StartTime};
2294
- FoundSomething($et, $tagTbl, $sampleTime);
2295
- $et->HandleTag($tagTbl, GPSDateTime => $time);
2296
- $et->HandleTag($tagTbl, GPSLatitude => $lat * ($latRef eq 'S' ? -1 : 1));
2297
- $et->HandleTag($tagTbl, GPSLongitude => $lon * ($lonRef eq 'W' ? -1 : 1));
2298
- if (defined $spd) {
2299
- $et->HandleTag($tagTbl, GPSSpeed => $spd);
2300
- $et->HandleTag($tagTbl, GPSSpeedRef => 'K');
2277
+ my ($rtnVal, %fix);
2278
+ # parse only RMC and GGA sentence [with leading timecode] for now
2279
+ for (;;) {
2280
+ my ($tc, $type, $tim);
2281
+ if ($$dataPt =~ /(?:\[(\d+)\])?\$[A-Z]{2}(RMC|GGA),(\d{2}\d{2}\d+(\.\d*)?),/g) {
2282
+ ($tc, $type, $tim) = ($1, $2, $3);
2283
+ }
2284
+ # write out last fix now if complete
2285
+ # (use the GPS timestamps because they may be different for the same timecode)
2286
+ if ($fix{tim} and (not $tim or $fix{tim} != $tim)) {
2287
+ if ($fix{dat} and defined $fix{lat} and defined $fix{lon}) {
2288
+ my $sampleTime;
2289
+ $sampleTime = ($fix{tc} - $$et{StartTime}) / 1000 if $fix{tc} and $$et{StartTime};
2290
+ FoundSomething($et, $tagTbl, $sampleTime);
2291
+ $et->HandleTag($tagTbl, GPSDateTime => $fix{dat});
2292
+ $et->HandleTag($tagTbl, GPSLatitude => $fix{lat});
2293
+ $et->HandleTag($tagTbl, GPSLongitude => $fix{lon});
2294
+ if (defined $fix{spd}) {
2295
+ $et->HandleTag($tagTbl, GPSSpeed => $fix{spd} * $knotsToKph);
2296
+ $et->HandleTag($tagTbl, GPSSpeedRef => 'K');
2297
+ }
2298
+ if (defined $fix{trk}) {
2299
+ $et->HandleTag($tagTbl, GPSTrack => $fix{trk});
2300
+ $et->HandleTag($tagTbl, GPSTrackRef => 'T');
2301
+ }
2302
+ $et->HandleTag($tagTbl, GPSAltitude => $fix{alt}) if defined $fix{alt};
2303
+ $et->HandleTag($tagTbl, GPSSatellites => $fix{nsats}+0) if defined $fix{nsats};
2304
+ $et->HandleTag($tagTbl, GPSDOP => $fix{hdop}) if defined $fix{hdop};
2305
+ }
2306
+ undef %fix;
2301
2307
  }
2302
- if (defined $trk) {
2303
- $et->HandleTag($tagTbl, GPSTrack => $trk);
2304
- $et->HandleTag($tagTbl, GPSTrackRef => 'T');
2308
+ $fix{tim} = $tim or last;
2309
+ my $pos = pos($$dataPt);
2310
+ pos($$dataPt) = $pos - length($tim) - 1; # rewind to re-parse time
2311
+ # (parsing of NMEA strings copied from Geotag.pm)
2312
+ if ($type eq 'RMC' and
2313
+ $$dataPt =~ /\G(\d{2})(\d{2})(\d+(\.\d*)?),A?,(\d*?)(\d{1,2}\.\d+),([NS]),(\d*?)(\d{1,2}\.\d+),([EW]),(\d*\.?\d*),(\d*\.?\d*),(\d{2})(\d{2})(\d+)/g)
2314
+ {
2315
+ my $year = $15 + ($15 >= 70 ? 1900 : 2000);
2316
+ $fix{tc} = $tc; # use timecode of RMC sentence
2317
+ $fix{dat} = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%sZ',$year,$14,$13,$1,$2,$3);
2318
+ $fix{lat} = (($5 || 0) + $6/60) * ($7 eq 'N' ? 1 : -1);
2319
+ $fix{lon} = (($8 || 0) + $9/60) * ($10 eq 'E' ? 1 : -1);
2320
+ $fix{spd} = $11 if length $11;
2321
+ $fix{trk} = $12 if length $12;
2322
+ } elsif ($type eq 'GGA' and
2323
+ $$dataPt =~ /\G(\d{2})(\d{2})(\d+(\.\d*)?),(\d*?)(\d{1,2}\.\d+),([NS]),(\d*?)(\d{1,2}\.\d+),([EW]),[1-6]?,(\d+)?,(\.\d+|\d+\.?\d*)?,(-?\d+\.?\d*)?,M?/g)
2324
+ {
2325
+ $fix{lat} = (($5 || 0) + $6/60) * ($7 eq 'N' ? 1 : -1);
2326
+ $fix{lon} = (($8 || 0) + $9/60) * ($10 eq 'E' ? 1 : -1);
2327
+ @fix{qw(nsats hdop alt)} = ($11,$12,$13);
2328
+ } else {
2329
+ pos($$dataPt) = $pos; # continue searching from our last match
2305
2330
  }
2306
2331
  }
2307
2332
  delete $$et{DOC_NUM};
2308
- return 1;
2333
+ return $rtnVal;
2334
+ }
2335
+
2336
+ #------------------------------------------------------------------------------
2337
+ # Process 'gps ' or 'udat' atom possibly containing NMEA (ref PH)
2338
+ # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
2339
+ # Returns: 1 on success
2340
+ sub ProcessGPSLog($$$)
2341
+ {
2342
+ my ($et, $dirInfo, $tagTbl) = @_;
2343
+ my $dataPt = $$dirInfo{DataPt};
2344
+ my ($rtnVal, @a);
2345
+
2346
+ # try NMEA format first
2347
+ return 1 if ProcessNMEA($et,$dirInfo,$tagTbl);
2348
+
2349
+ # DENVER ACG-8050WMK2 format looks like this:
2350
+ # 210318073213[1][N][52200970][E][006362321][+00152][100][00140][C000000]+000+000+000+000+000+000+000+000+000+000+000+000+000+000+000+000+000+000
2351
+ # YYMMDDHHMMSS A? NS lat EW lon alt kph dir kCal accel
2352
+ while ($$dataPt =~ /\b(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\[1\]\[([NS])\]\[(\d{8})\]\[([EW])\]\[(\d{9})\]\[([-+]?\d*)\]\[(\d*)\]\[(\d*)\]\[C?(\d*)\](([-+]\d{3})+)/g) {
2353
+ my $lat = substr( $8,0,2) + substr( $8,2) / 600000;
2354
+ my $lon = substr($10,0,3) + substr($10,3) / 600000;
2355
+ $$et{DOC_NUM} = ++$$et{DOC_COUNT};
2356
+ $et->HandleTag($tagTbl, GPSDateTime => "20$1:$2:$3 $4:$5:$6Z");
2357
+ $et->HandleTag($tagTbl, GPSLatitude => $lat * ($7 eq 'S' ? -1 : 1));
2358
+ $et->HandleTag($tagTbl, GPSLongitude => $lon * ($9 eq 'W' ? -1 : 1));
2359
+ $et->HandleTag($tagTbl, GPSAltitude => $11 / 10) if length $11;
2360
+ $et->HandleTag($tagTbl, GPSSpeed => $12 + 0) if length $12;
2361
+ $et->HandleTag($tagTbl, GPSTrack => $13 + 0) if length $13;
2362
+ $et->HandleTag($tagTbl, KiloCalories => $14 / 10) if length $14;
2363
+ $et->HandleTag($tagTbl, Accelerometer=> $15) if length $15;
2364
+ $rtnVal = 1;
2365
+ }
2366
+ delete $$et{DOC_NUM};
2367
+ return $rtnVal;
2309
2368
  }
2310
2369
 
2311
2370
  #------------------------------------------------------------------------------