exiftool-vendored.pl 12.91.0 → 12.96.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,7 +16,7 @@ use strict;
16
16
  use vars qw($VERSION $AUTOLOAD);
17
17
  use Image::ExifTool qw(:DataAccess :Utils);
18
18
 
19
- $VERSION = '1.45';
19
+ $VERSION = '1.46';
20
20
 
21
21
  sub WritePS($$);
22
22
  sub ProcessPS($$;$);
@@ -156,15 +156,6 @@ sub AUTOLOAD
156
156
  return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
157
157
  }
158
158
 
159
- #------------------------------------------------------------------------------
160
- # Is this a PC system
161
- # Returns: true for PC systems
162
- my %isPC = (MSWin32 => 1, os2 => 1, dos => 1, NetWare => 1, symbian => 1, cygwin => 1);
163
- sub IsPC()
164
- {
165
- return $isPC{$^O};
166
- }
167
-
168
159
  #------------------------------------------------------------------------------
169
160
  # Get image width or height
170
161
  # Inputs: 0) value list ref (ImageData, BoundingBox), 1) true to get height
@@ -284,7 +275,7 @@ sub GetNextLine($$)
284
275
  $$raf{PSEnd} and CheckPSEnd($raf, \$data);
285
276
  # split line if it contains other newline sequences
286
277
  if ($data =~ /$altnl/) {
287
- if (length($data) > 500000 and IsPC()) {
278
+ if (length($data) > 500000 and Image::ExifTool::IsPC()) {
288
279
  # patch for Windows memory problem
289
280
  unless ($changedNL) {
290
281
  $changedNL = $/;
@@ -537,7 +528,7 @@ sub ProcessPS($$;$)
537
528
  $raf->ReadLine($data) or last;
538
529
  # check for alternate newlines as efficiently as possible
539
530
  if ($data =~ /$altnl/) {
540
- if (length($data) > 500000 and IsPC()) {
531
+ if (length($data) > 500000 and Image::ExifTool::IsPC()) {
541
532
  # Windows can't split very long lines due to poor memory handling,
542
533
  # so re-read the file with the other newline character instead
543
534
  # (slower but uses less memory)
@@ -48,7 +48,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
48
48
  use Image::ExifTool::Exif;
49
49
  use Image::ExifTool::GPS;
50
50
 
51
- $VERSION = '2.99';
51
+ $VERSION = '3.02';
52
52
 
53
53
  sub ProcessMOV($$;$);
54
54
  sub ProcessKeys($$$);
@@ -6552,7 +6552,7 @@ my %userDefined = (
6552
6552
  PROCESS_PROC => \&ProcessKeys,
6553
6553
  WRITE_PROC => \&WriteKeys,
6554
6554
  CHECK_PROC => \&CheckQTValue,
6555
- VARS => { LONG_TAGS => 7 },
6555
+ VARS => { LONG_TAGS => 8 },
6556
6556
  WRITABLE => 1,
6557
6557
  # (not PREFERRED when writing)
6558
6558
  GROUPS => { 1 => 'Keys' },
@@ -6606,6 +6606,8 @@ my %userDefined = (
6606
6606
  PrintConv => '$val * 1e6 . " microseconds"',
6607
6607
  PrintConvInv => '$val =~ s/ .*//; $val * 1e-6',
6608
6608
  },
6609
+ # 'camera.focal_length.35mm_equivalent' - not top level (written to Keys in video track)
6610
+ # 'camera.lens_model' - not top level (written to Keys in video track)
6609
6611
  'location.ISO6709' => {
6610
6612
  Name => 'GPSCoordinates',
6611
6613
  Groups => { 2 => 'Location' },
@@ -6668,7 +6670,12 @@ my %userDefined = (
6668
6670
  #
6669
6671
  'com.apple.photos.captureMode' => 'CaptureMode',
6670
6672
  'com.android.version' => 'AndroidVersion',
6671
- 'com.android.capture.fps' => 'AndroidCaptureFPS',
6673
+ 'com.android.capture.fps' => { Name => 'AndroidCaptureFPS', Writable => 'float' },
6674
+ 'com.android.manufacturer' => 'AndroidMake',
6675
+ 'com.android.model' => 'AndroidModel',
6676
+ 'com.xiaomi.preview_video_cover' => { Name => 'XiaomiPreviewVideoCover', Writable => 'int32s' },
6677
+ 'xiaomi.exifInfo.videoinfo' => 'XiaomiExifInfo',
6678
+ 'com.xiaomi.hdr10' => { Name => 'XiaomiHDR10', Writable => 'int32s' },
6672
6679
  #
6673
6680
  # also seen
6674
6681
  #
@@ -9484,7 +9491,7 @@ sub ProcessKeys($$$)
9484
9491
  $$newInfo{KeysID} = $tag; # save original ID for use in family 7 group name
9485
9492
  AddTagToTable($itemList, $id, $newInfo);
9486
9493
  $msg or $msg = '';
9487
- $out and print $out "$$et{INDENT}Added ItemList Tag $id = ($ns) $tag$msg\n";
9494
+ $out and print $out "$$et{INDENT}Added ItemList Tag $id = ($ns) $full$msg\n";
9488
9495
  }
9489
9496
  $pos += $len;
9490
9497
  ++$index;
@@ -9521,7 +9528,7 @@ sub ProcessMOV($$;$)
9521
9528
  my $dirID = $$dirInfo{DirID} || '';
9522
9529
  my $charsetQuickTime = $et->Options('CharsetQuickTime');
9523
9530
  my ($buff, $tag, $size, $track, $isUserData, %triplet, $doDefaultLang, $index);
9524
- my ($dirEnd, $unkOpt, %saveOptions, $atomCount, $warnStr);
9531
+ my ($dirEnd, $unkOpt, %saveOptions, $atomCount, $warnStr, $trailer);
9525
9532
 
9526
9533
  my $topLevel = not $$et{InQuickTime};
9527
9534
  $$et{InQuickTime} = 1;
@@ -9550,6 +9557,17 @@ sub ProcessMOV($$;$)
9550
9557
  $tagTablePtr = GetTagTable('Image::ExifTool::QuickTime::Main');
9551
9558
  }
9552
9559
  ($size, $tag) = unpack('Na4', $buff);
9560
+ my $fast = $$et{OPTIONS}{FastScan} || 0;
9561
+ # check for Insta360 trailer
9562
+ if ($topLevel and not $fast) {
9563
+ my $pos = $raf->Tell();
9564
+ if ($raf->Seek(-40, 2) and $raf->Read($buff, 40) == 40 and
9565
+ substr($buff, 8) eq '8db42d694ccc418790edff439fe026bf')
9566
+ {
9567
+ $trailer = [ 'Insta360', $raf->Tell() - unpack('V',$buff) ];
9568
+ }
9569
+ $raf->Seek($pos,0) or return 0;
9570
+ }
9553
9571
  if ($dataPt) {
9554
9572
  $verbose and $et->VerboseDir($$dirInfo{DirName});
9555
9573
  } else {
@@ -9588,7 +9606,6 @@ sub ProcessMOV($$;$)
9588
9606
  # have XMP take priority except for HEIC
9589
9607
  $$et{PRIORITY_DIR} = 'XMP' unless $fileType and $fileType eq 'HEIC';
9590
9608
  }
9591
- my $fast = $$et{OPTIONS}{FastScan} || 0;
9592
9609
  $$raf{NoBuffer} = 1 if $fast; # disable buffering in FastScan mode
9593
9610
 
9594
9611
  my $ee = $$et{OPTIONS}{ExtractEmbedded};
@@ -10129,6 +10146,10 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
10129
10146
  $dataPos += $size + 8; # point to start of next atom data
10130
10147
  last if $dirEnd and $dataPos >= $dirEnd; # (note: ignores last value if 0 bytes)
10131
10148
  $lastPos = $raf->Tell() + $dirBase;
10149
+ if ($trailer and $lastPos >= $$trailer[1]) {
10150
+ $et->Warn(sprintf('%s trailer at offset 0x%x', @$trailer), 1);
10151
+ last;
10152
+ }
10132
10153
  $raf->Read($buff, 8) == 8 or last;
10133
10154
  $lastTag = $tag if $$tagTablePtr{$tag} and $tag ne 'free'; # (Insta360 sometimes puts free block before trailer)
10134
10155
  ($size, $tag) = unpack('Na4', $buff);
@@ -10137,14 +10158,10 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
10137
10158
  if ($warnStr) {
10138
10159
  # assume this is an unknown trailer if it comes immediately after
10139
10160
  # mdat or moov and has a tag name we don't recognize
10140
- if (($lastTag eq 'mdat' or $lastTag eq 'moov') and (not $$tagTablePtr{$tag} or
10141
- ref $$tagTablePtr{$tag} eq 'HASH' and $$tagTablePtr{$tag}{Unknown}))
10161
+ if (($lastTag eq 'mdat' or $lastTag eq 'moov') and
10162
+ (not $$tagTablePtr{$tag} or ref $$tagTablePtr{$tag} eq 'HASH' and $$tagTablePtr{$tag}{Unknown}))
10142
10163
  {
10143
- if ($size == 0x1000000 - 8 and $tag =~ /^(\x94\xc0\x7e\0|\0\x02\0\0)/) {
10144
- $et->Warn(sprintf('Insta360 trailer at offset 0x%x', $lastPos), 1);
10145
- } else {
10146
- $et->Warn('Unknown trailer with '.lcfirst($warnStr));
10147
- }
10164
+ $et->Warn('Unknown trailer with '.lcfirst($warnStr));
10148
10165
  } else {
10149
10166
  $et->Warn($warnStr);
10150
10167
  }
@@ -10171,7 +10188,10 @@ QTLang: foreach $tag (@{$$et{QTLang}}) {
10171
10188
  for ($i=0, $key=$name; $$infoHash{$key}; ++$i, $key="$name ($i)") {
10172
10189
  next QTLang if $et->GetGroup($key, 0) eq 'QuickTime';
10173
10190
  }
10174
- $et->FoundTag($tagInfo, $$et{VALUE}{$tag});
10191
+ $key = $et->FoundTag($tagInfo, $$et{VALUE}{$tag});
10192
+ # copy extra tag information (groups, etc) to the synthetic tag
10193
+ $$et{TAG_EXTRA}{$key} = $$et{TAG_EXTRA}{$tag};
10194
+ $et->VPrint(0, "(synthesized default-language tag for QuickTime:$$tagInfo{Name})");
10175
10195
  }
10176
10196
  delete $$et{QTLang};
10177
10197
  }
@@ -109,7 +109,7 @@ my %insvLimit = (
109
109
  The tags below are extracted from timed metadata in QuickTime and other
110
110
  formats of video files when the ExtractEmbedded option is used. Although
111
111
  most of these tags are combined into the single table below, ExifTool
112
- currently reads 76 different formats of timed GPS metadata from video files.
112
+ currently reads 77 different formats of timed GPS metadata from video files.
113
113
  },
114
114
  VARS => { NO_ID => 1 },
115
115
  GPSLatitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', RawConv => '$$self{FoundGPSLatitude} = 1; $val' },
@@ -757,6 +757,15 @@ my %insvLimit = (
757
757
  10 => { Name => 'FusionYPR', Format => 'float[3]' },
758
758
  );
759
759
 
760
+ #------------------------------------------------------------------------------
761
+ # Convert unsigned 32-bit integer to signed
762
+ # Inputs: <none> (uses value in $_)
763
+ # Returns: signed integer
764
+ sub SignedInt32()
765
+ {
766
+ return $_ < 0x80000000 ? $_ : $_ - 4294967296;
767
+ }
768
+
760
769
  #------------------------------------------------------------------------------
761
770
  # Save information from keys in OtherSampleDesc directory for processing timed metadata
762
771
  # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
@@ -1474,6 +1483,31 @@ sub ConvertLatLon($$)
1474
1483
  $_[1] = $deg + ($_[1] - $deg * 100) / 60;
1475
1484
  }
1476
1485
 
1486
+ #------------------------------------------------------------------------------
1487
+ # Decrypt Lucky data
1488
+ # Inputs: 0) string to decrypt, 1) encryption key
1489
+ # Returns: decrypted string
1490
+ my @luckyKeys = ('luckychip gps', 'customer ## gps');
1491
+ sub DecryptLucky($$) {
1492
+ my ($str, $key) = @_;
1493
+ my @str = unpack('C*', $str);
1494
+ my @key = unpack('C*', $key);
1495
+ my @enc = (0..255);
1496
+ my ($i, $j, $k) = (0, 0, 0);
1497
+ do {
1498
+ $j = ($j + $enc[$i] + $key[$i % length($key)]) & 0xff;
1499
+ @enc[$i,$j] = @enc[$j,$i];
1500
+ } while (++$i < 256);
1501
+ ($i, $j, $k) = (0, 0, 0);
1502
+ do {
1503
+ $j = ($j + 1) & 0xff;
1504
+ $k = ($k + $enc[$j]) & 0xff;
1505
+ @enc[$j,$k] = @enc[$k,$j];
1506
+ $str[$i] ^= $enc[($enc[$j] + $enc[$k]) & 0xff];
1507
+ } while (++$i < @str);
1508
+ return pack('C*', @str);
1509
+ }
1510
+
1477
1511
  #------------------------------------------------------------------------------
1478
1512
  # Process "freeGPS " data blocks
1479
1513
  # Inputs: 0) ExifTool ref, 1) dirInfo ref {DataPt,SampleTime,SampleDuration}, 2) tagTable ref
@@ -1589,40 +1623,87 @@ sub ProcessFreeGPS($$$)
1589
1623
  }
1590
1624
  if (defined $lat) {
1591
1625
  # extract accelerometer readings if GPS was valid
1592
- @acc = unpack('x68V3', $$dataPt);
1593
- # change to signed integer and divide by 256
1594
- map { $_ = $_ - 4294967296 if $_ >= 0x80000000; $_ /= 256 } @acc;
1626
+ # and change to signed integer and divide by 256
1627
+ @acc = map { SignedInt32 / 256 } unpack('x68V3', $$dataPt);
1595
1628
  }
1596
1629
 
1597
- } elsif ($$dataPt =~ /^.{37}\0\0\0A([NS])([EW])/s) {
1630
+ } elsif ($$dataPt =~ /^.{37}\0\0\0A([NS])([EW])\0/s) {
1598
1631
 
1599
- $debug and $et->FoundTag(GPSType => 3);
1600
- # decode freeGPS from ViofoA119v3 dashcam (similar to Novatek GPS format)
1601
- # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
1602
- # 0010: 05 00 00 00 2f 00 00 00 03 00 00 00 13 00 00 00 [..../...........]
1603
- # 0020: 09 00 00 00 1b 00 00 00 41 4e 57 00 25 d1 99 45 [........ANW.%..E]
1604
- # 0030: f1 47 40 46 66 66 d2 41 85 eb 83 41 00 00 00 00 [.G@Fff.A...A....]
1605
1632
  ($latRef, $lonRef) = ($1, $2);
1606
1633
  ($hr,$min,$sec,$yr,$mon,$day) = unpack('x16V6', $$dataPt);
1607
- if ($yr >= 2000) {
1608
- # Kenwood dashcam sometimes stores absolute year and local time
1609
- # (but sometimes year since 2000 and UTC time in same video!)
1610
- require Time::Local;
1611
- my $time = Image::ExifTool::TimeLocal($sec,$min,$hr,$day,$mon-1,$yr);
1612
- ($sec,$min,$hr,$day,$mon,$yr) = gmtime($time);
1613
- $yr += 1900;
1614
- ++$mon;
1615
- $et->WarnOnce('Converting GPSDateTime to UTC based on local time zone',1);
1616
- }
1617
- $lat = GetFloat($dataPt, 0x2c);
1618
- $lon = GetFloat($dataPt, 0x30);
1619
- $spd = GetFloat($dataPt, 0x34) * $knotsToKph; # (convert knots to km/h)
1620
- $trk = GetFloat($dataPt, 0x38);
1621
- # (may be all zeros or int16u counting from 1 to 6 if not valid)
1622
- my $tmp = substr($$dataPt, 60, 12);
1623
- if ($tmp ne "\0\0\0\0\0\0\0\0\0\0\0\0" and $tmp ne "\x01\0\x02\0\x03\0\x04\0\x05\0\x06\0") {
1624
- @acc = unpack('V3', $tmp);
1625
- map { $_ = $_ - 4294967296 if $_ >= 0x80000000; $_ /= 256 } @acc;
1634
+ # test for base64-encoded and encrypted lucky gps strings
1635
+ my ($notEnc, $notStr, $lt, $ln);
1636
+ if (length($$dataPt) < 0x78) {
1637
+ $notEnc = $notStr = 1;
1638
+ } else {
1639
+ $lt = substr($$dataPt, 0x2c, 20), # latitude
1640
+ $ln = substr($$dataPt, 0x40, 20), # longitude
1641
+ /^[A-Za-z0-9+\/]{8,20}={0,2}\0*$/ or $notEnc = 1, last foreach ($lt, $ln);
1642
+ /^\d{1,5}\.\d+\0*$/ or $notStr = 1, last foreach ($lt, $ln);
1643
+ }
1644
+ if ($notEnc and $notStr) {
1645
+
1646
+ $debug and $et->FoundTag(GPSType => '3a');
1647
+ # decode freeGPS from ViofoA119v3 dashcam (similar to Novatek GPS format)
1648
+ # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
1649
+ # 0010: 05 00 00 00 2f 00 00 00 03 00 00 00 13 00 00 00 [..../...........]
1650
+ # 0020: 09 00 00 00 1b 00 00 00 41 4e 57 00 25 d1 99 45 [........ANW.%..E]
1651
+ # 0030: f1 47 40 46 66 66 d2 41 85 eb 83 41 00 00 00 00 [.G@Fff.A...A....]
1652
+ if ($yr >= 2000) {
1653
+ # Kenwood dashcam sometimes stores absolute year and local time
1654
+ # (but sometimes year since 2000 and UTC time in same video!)
1655
+ require Time::Local;
1656
+ my $time = Image::ExifTool::TimeLocal($sec,$min,$hr,$day,$mon-1,$yr);
1657
+ ($sec,$min,$hr,$day,$mon,$yr) = gmtime($time);
1658
+ $yr += 1900;
1659
+ ++$mon;
1660
+ $et->WarnOnce('Converting GPSDateTime to UTC based on local time zone',1);
1661
+ }
1662
+ $lat = GetFloat($dataPt, 0x2c);
1663
+ $lon = GetFloat($dataPt, 0x30);
1664
+ $spd = GetFloat($dataPt, 0x34) * $knotsToKph;
1665
+ $trk = GetFloat($dataPt, 0x38);
1666
+ # (may be all zeros or int16u counting from 1 to 6 if not valid)
1667
+ my $tmp = substr($$dataPt, 60, 12);
1668
+ if ($tmp ne "\0\0\0\0\0\0\0\0\0\0\0\0" and $tmp ne "\x01\0\x02\0\x03\0\x04\0\x05\0\x06\0") {
1669
+ @acc = map { SignedInt32 / 256 } unpack('V3', $tmp);
1670
+ }
1671
+
1672
+ } else {
1673
+
1674
+ $debug and $et->FoundTag(GPSType => '3b');
1675
+ # decode freeGPS from E-ACE B44 dashcam
1676
+ # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
1677
+ # 0010: 08 00 00 00 22 00 00 00 01 00 00 00 18 00 00 00 [...."...........]
1678
+ # 0020: 08 00 00 00 10 00 00 00 41 4e 45 00 67 4e 69 69 [........ANE.gNii]
1679
+ # 0030: 5a 38 4a 54 74 48 63 61 36 74 77 3d 00 00 00 00 [Z8JTtHca6tw=....]
1680
+ # 0040: 68 74 75 69 5a 4d 4a 53 73 58 55 58 37 4e 6f 3d [htuiZMJSsXUX7No=]
1681
+ # 0050: 00 00 00 00 64 3b ac 41 e1 3a 1d 43 2b 01 00 00 [....d;.A.:.C+...]
1682
+ # 0060: fd ff ff ff 43 00 00 00 32 4a 37 31 50 70 55 48 [....C...2J71PpUH]
1683
+ # 0070: 37 69 68 66 00 00 00 00 00 00 00 00 00 00 00 00 [7ihf............]
1684
+ # (16-byte string at 0x68 is base64 encoded and encrypted 'luckychip' string)
1685
+ $spd = GetFloat($dataPt, 0x54) * $knotsToKph;
1686
+ $trk = GetFloat($dataPt, 0x58);
1687
+ @acc = map SignedInt32, unpack('x92V3', $$dataPt);
1688
+ # (accelerometer scaling is roughly 1G=250-300, but it varies depending on the axis,
1689
+ # so leave the values as raw. The axes are positive acceleration up,left,forward)
1690
+ if ($notEnc) { # (not encrypted)
1691
+ ($lat = $lt) =~ s/\0+$//;
1692
+ ($lon = $ln) =~ s/\0+$//;
1693
+ } else {
1694
+ # decode base64 strings
1695
+ require Image::ExifTool::XMP;
1696
+ $_ = ${Image::ExifTool::XMP::DecodeBase64($_)} foreach ($lt, $ln);
1697
+ # try various keys to decrypt lat/lon
1698
+ my ($i, $ch, $key) = (0, 'a', $luckyKeys[0]);
1699
+ for (; $i<20; ++$i) {
1700
+ $i and ($key = $luckyKeys[1]) =~ s/#/$ch/g, ++$ch;
1701
+ ($lat = DecryptLucky($lt, $key)) =~ /^\d{1,4}\.\d+$/ or undef($lat), next;
1702
+ ($lon = DecryptLucky($ln, $key)) =~ /^\d{1,5}\.\d+$/ or undef($lon), next;
1703
+ last;
1704
+ }
1705
+ $lon or $et->WarnOnce('Unknown encryption for latitude/longitude');
1706
+ }
1626
1707
  }
1627
1708
 
1628
1709
  } elsif ($$dataPt =~ /^.{21}\0\0\0A([NS])([EW])/s) {
@@ -1683,7 +1764,7 @@ sub ProcessFreeGPS($$$)
1683
1764
  $trk -= 360 if $trk >= 360;
1684
1765
  undef @acc;
1685
1766
  } else {
1686
- map { $_ = $_ - 4294967296 if $_ >= 0x80000000; $_ /= 1000 } @acc; # (NC)
1767
+ @acc = map { SignedInt32 / 1000 } @acc; # (NC)
1687
1768
  }
1688
1769
 
1689
1770
  } elsif ($$dataPt =~ /^.{60}4W`b]S</s and length($$dataPt) >= 140) {
@@ -1787,7 +1868,7 @@ sub ProcessFreeGPS($$$)
1787
1868
  return 0;
1788
1869
  }
1789
1870
  # (not sure about acc scaling)
1790
- map { $_ = $_ - 4294967296 if $_ >= 0x80000000; $_ /= 1000 } @acc;
1871
+ @acc = map { SignedInt32 / 1000 } @acc;
1791
1872
  $lon = GetFloat($dataPt, 0x5c);
1792
1873
  $lat = GetFloat($dataPt, 0x60);
1793
1874
  $spd = GetFloat($dataPt, 0x64) * $knotsToKph;
@@ -1932,7 +2013,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
1932
2013
  # 0x7c - int32s[3] accelerometer * 1000
1933
2014
  ($latRef, $lonRef) = ($1, $2);
1934
2015
  ($hr,$min,$sec,$yr,$mon,$day,@acc) = unpack('x48V3x52V6', $$dataPt);
1935
- map { $_ = $_ - 4294967296 if $_ >= 0x80000000; $_ /= 1000 } @acc;
2016
+ @acc = map { SignedInt32 / 1000 } @acc;
1936
2017
  $lat = GetDouble($dataPt, 0x40);
1937
2018
  $lon = GetDouble($dataPt, 0x50);
1938
2019
  $spd = GetDouble($dataPt, 0x60) * $knotsToKph;
@@ -1948,8 +2029,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
1948
2029
  $lon = abs(GetFloat(\$dat, 8)); # (abs just to be safe)
1949
2030
  $spd = GetFloat(\$dat, 12) * $knotsToKph;
1950
2031
  $trk = GetFloat(\$dat, 16);
1951
- @acc = unpack('x20V3', $dat);
1952
- map { $_ = $_ - 4294967296 if $_ >= 0x80000000 } @acc;
2032
+ @acc = map SignedInt32, unpack('x20V3', $dat);
1953
2033
  ConvertLatLon($lat, $lon);
1954
2034
  $$et{DOC_NUM} = ++$$et{DOC_COUNT};
1955
2035
  $et->HandleTag($tagTbl, GPSLatitude => $lat * (substr($dat,1,1) eq 'S' ? -1 : 1));
@@ -1983,7 +2063,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
1983
2063
  $lon = abs(GetDouble($dataPt, 48)); # (abs just to be safe)
1984
2064
  $spd = GetDouble($dataPt, 64) * $knotsToKph;
1985
2065
  $trk = GetDouble($dataPt, 72);
1986
- map { $_ = $_ - 4294967296 if $_ >= 0x80000000; $_ /= 1000 } @acc; # (NC)
2066
+ @acc = map { SignedInt32 / 1000 } @acc; # (NC)
1987
2067
  # (not necessary to read RMC sentence because we already have it all)
1988
2068
 
1989
2069
  } elsif ($$dataPt =~ /^.{72}A[NS][EW]\0/s) {
@@ -2117,7 +2197,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2117
2197
  $day < 1 or $day > 31 or
2118
2198
  $hr > 59 or $min > 59 or $sec > 600;
2119
2199
  # change lat/lon to signed integer and divide by 1e7
2120
- map { $_ = $_ - 4294967296 if $_ >= 0x80000000; $_ /= 1e7 } $lat, $lon;
2200
+ ($lat, $lon) = map { SignedInt32 / 1e7 } $lat, $lon;
2121
2201
  $trk -= 0x10000 if $trk >= 0x8000; # make it signed
2122
2202
  $trk /= 100;
2123
2203
  $trk += 360 if $trk < 0;
@@ -22,7 +22,7 @@ use vars qw($VERSION %samsungLensTypes);
22
22
  use Image::ExifTool qw(:DataAccess :Utils);
23
23
  use Image::ExifTool::Exif;
24
24
 
25
- $VERSION = '1.57';
25
+ $VERSION = '1.58';
26
26
 
27
27
  sub WriteSTMN($$$);
28
28
  sub ProcessINFO($$$);
@@ -957,7 +957,7 @@ my %formatMinMax = (
957
957
  Samsung models such as the Galaxy S4 and Tab S, and from the 'sefd' atom in
958
958
  HEIC images from models such as the S10+.
959
959
  },
960
- '0x0001-name' => 'EmbeddedImageName', # ("DualShot_1","DualShot_2")
960
+ '0x0001-name' => 'EmbeddedImageName', # ("DualShot_1","DualShot_2","SingleShot")
961
961
  '0x0001' => [
962
962
  {
963
963
  Name => 'EmbeddedImage',
@@ -970,6 +970,7 @@ my %formatMinMax = (
970
970
  Groups => { 2 => 'Preview' },
971
971
  Binary => 1,
972
972
  },
973
+ # (have also seen the string "BOKEH" here (SM-A226B)
973
974
  ],
974
975
  '0x0100-name' => 'EmbeddedAudioFileName', # ("SoundShot_000")
975
976
  '0x0100' => { Name => 'EmbeddedAudioFile', Groups => { 2 => 'Audio' }, Binary => 1 },
@@ -1265,6 +1266,7 @@ my %formatMinMax = (
1265
1266
  '0x0b40' => { # (SM-N975X front camera)
1266
1267
  Name => 'SingleShotMeta',
1267
1268
  SubDirectory => { TagTable => 'Image::ExifTool::Samsung::SingleShotMeta' },
1269
+ # (have also see the string "BOKEH_INFO" here (SM-A226B)
1268
1270
  },
1269
1271
  # 0x0b41-name - seen 'SingeShot_DepthMap_1' (Yes, "Singe") (SM-N975X front camera)
1270
1272
  '0x0b41' => { Name => 'SingleShotDepthMap', Binary => 1 },
@@ -1493,7 +1495,7 @@ sub ProcessSamsungMeta($$$)
1493
1495
  my $pos = $$dirInfo{DirStart};
1494
1496
  my $end = $$dirInfo{DirLen} + $pos;
1495
1497
  unless ($pos + 8 <= $end and substr($$dataPt, $pos, 4) eq 'DOFS') {
1496
- $et->Warn("Unrecognized $dirName data");
1498
+ $et->Warn("Unrecognized $dirName data", 1);
1497
1499
  return 0;
1498
1500
  }
1499
1501
  my $ver = Get32u($dataPt, $pos + 4);