exiftool-vendored.pl 12.85.0 → 12.91.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.
Files changed (49) hide show
  1. package/bin/Changes +87 -0
  2. package/bin/MANIFEST +1 -0
  3. package/bin/META.json +1 -1
  4. package/bin/META.yml +16 -16
  5. package/bin/README +3 -2
  6. package/bin/build_geolocation +7 -4
  7. package/bin/build_tag_lookup +29 -11
  8. package/bin/config_files/example.config +5 -0
  9. package/bin/config_files/onone.config +28 -0
  10. package/bin/exiftool +23 -15
  11. package/bin/lib/Image/ExifTool/AIFF.pm +8 -4
  12. package/bin/lib/Image/ExifTool/ASF.pm +4 -1
  13. package/bin/lib/Image/ExifTool/BuildTagLookup.pm +14 -8
  14. package/bin/lib/Image/ExifTool/Canon.pm +102 -12
  15. package/bin/lib/Image/ExifTool/CanonRaw.pm +1 -1
  16. package/bin/lib/Image/ExifTool/CanonVRD.pm +5 -2
  17. package/bin/lib/Image/ExifTool/FujiFilm.pm +46 -4
  18. package/bin/lib/Image/ExifTool/Geolocation.dat +0 -0
  19. package/bin/lib/Image/ExifTool/Geolocation.pm +6 -0
  20. package/bin/lib/Image/ExifTool/ID3.pm +39 -33
  21. package/bin/lib/Image/ExifTool/InDesign.pm +8 -4
  22. package/bin/lib/Image/ExifTool/Jpeg2000.pm +0 -1
  23. package/bin/lib/Image/ExifTool/Lang/de.pm +2 -2
  24. package/bin/lib/Image/ExifTool/Matroska.pm +66 -10
  25. package/bin/lib/Image/ExifTool/MinoltaRaw.pm +2 -2
  26. package/bin/lib/Image/ExifTool/Nikon.pm +36 -2
  27. package/bin/lib/Image/ExifTool/Olympus.pm +27 -17
  28. package/bin/lib/Image/ExifTool/PNG.pm +10 -2
  29. package/bin/lib/Image/ExifTool/Panasonic.pm +1 -0
  30. package/bin/lib/Image/ExifTool/PanasonicRaw.pm +1 -0
  31. package/bin/lib/Image/ExifTool/Pentax.pm +80 -14
  32. package/bin/lib/Image/ExifTool/QuickTime.pm +58 -13
  33. package/bin/lib/Image/ExifTool/QuickTimeStream.pl +111 -8
  34. package/bin/lib/Image/ExifTool/RIFF.pm +20 -10
  35. package/bin/lib/Image/ExifTool/Samsung.pm +56 -19
  36. package/bin/lib/Image/ExifTool/Sony.pm +21 -11
  37. package/bin/lib/Image/ExifTool/TagLookup.pm +6808 -6788
  38. package/bin/lib/Image/ExifTool/TagNames.pod +120 -55
  39. package/bin/lib/Image/ExifTool/WriteIPTC.pl +1 -1
  40. package/bin/lib/Image/ExifTool/WriteQuickTime.pl +84 -15
  41. package/bin/lib/Image/ExifTool/Writer.pl +7 -4
  42. package/bin/lib/Image/ExifTool/XMP.pm +8 -8
  43. package/bin/lib/Image/ExifTool/XMP2.pl +51 -30
  44. package/bin/lib/Image/ExifTool/ZIP.pm +8 -4
  45. package/bin/lib/Image/ExifTool.pm +65 -42
  46. package/bin/lib/Image/ExifTool.pod +28 -20
  47. package/bin/perl-Image-ExifTool.spec +1 -1
  48. package/bin/pp_build_exe.args +4 -4
  49. package/package.json +3 -3
@@ -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.97';
51
+ $VERSION = '2.99';
52
52
 
53
53
  sub ProcessMOV($$;$);
54
54
  sub ProcessKeys($$$);
@@ -238,7 +238,11 @@ my %useExt = ( GLV => 'MP4' );
238
238
 
239
239
  # information for int32u date/time tags (time zero is Jan 1, 1904)
240
240
  my %timeInfo = (
241
- Notes => 'converted from UTC to local time if the QuickTimeUTC option is set',
241
+ Notes => q{
242
+ converted from UTC to local time if the QuickTimeUTC option is set. This
243
+ tag is part of a binary data structure so it may not be deleted -- instead
244
+ the value is set to zero if the tag is deleted individually
245
+ },
242
246
  Shift => 'Time',
243
247
  Writable => 1,
244
248
  Permanent => 1,
@@ -503,6 +507,11 @@ my %eeBox2 = (
503
507
  # image types in AVIF and HEIC files
504
508
  my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
505
509
 
510
+ my %userDefined = (
511
+ ALBUMARTISTSORT => 'AlbumArtistSort',
512
+ ASIN => 'ASIN',
513
+ );
514
+
506
515
  # QuickTime atoms
507
516
  %Image::ExifTool::QuickTime::Main = (
508
517
  PROCESS_PROC => \&ProcessMOV,
@@ -2336,10 +2345,8 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
2336
2345
  Name => 'RicohInfo',
2337
2346
  Condition => '$$valPt =~ /^\xff\xe1..Exif\0\0/s',
2338
2347
  SubDirectory => {
2339
- TagTable => 'Image::ExifTool::Exif::Main',
2340
- ProcessProc => \&Image::ExifTool::ProcessTIFF,
2341
- Start => 10,
2342
- Base => '$start',
2348
+ TagTable => 'Image::ExifTool::JPEG::Main',
2349
+ ProcessProc => \&Image::ExifTool::ProcessJPEG,
2343
2350
  }
2344
2351
  },
2345
2352
  RTHU => { #PH (GR)
@@ -8985,12 +8992,20 @@ sub HandleItemInfo($)
8985
8992
  if ($$item{Extents} and @{$$item{Extents}}) {
8986
8993
  $len += $$_[2] foreach @{$$item{Extents}};
8987
8994
  }
8988
- $et->VPrint(0, "$$et{INDENT}Item $id) '${type}' ($len bytes)\n");
8995
+ my $enc = $$item{ContentEncoding} ? ", $$item{ContentEncoding} encoded" : '';
8996
+ $et->VPrint(0, "$$et{INDENT}Item $id) '${type}' ($len bytes$enc)\n");
8989
8997
  }
8990
8998
  # get ExifTool name for this item
8991
8999
  my $name = { Exif => 'EXIF', 'application/rdf+xml' => 'XMP', jpeg => 'PreviewImage' }->{$type} || '';
8992
9000
  my ($warn, $extent);
8993
- $warn = "Can't currently decode encoded $type metadata" if $$item{ContentEncoding};
9001
+ if ($$item{ContentEncoding}) {
9002
+ if ($$item{ContentEncoding} ne 'deflate') {
9003
+ # (other possible values are 'gzip' and 'compress', but I don't have samples of these)
9004
+ $warn = "Can't currently decode $$item{ContentEncoding} encoded $type metadata";
9005
+ } elsif (not eval { require Compress::Zlib }) {
9006
+ $warn = "Install Compress::Zlib to decode deflated $type metadata";
9007
+ }
9008
+ }
8994
9009
  $warn = "Can't currently decode protected $type metadata" if $$item{ProtectionIndex};
8995
9010
  $warn = "Can't currently extract $type with construction method $$item{ConstructionMethod}" if $$item{ConstructionMethod};
8996
9011
  $et->WarnOnce($warn) if $warn and $name;
@@ -9050,6 +9065,22 @@ sub HandleItemInfo($)
9050
9065
  next unless defined $buff;
9051
9066
  $buff = $val . $buff if length $val;
9052
9067
  next unless length $buff; # ignore empty directories
9068
+ if ($$item{ContentEncoding}) {
9069
+ my ($v2, $stat);
9070
+ my $inflate = Compress::Zlib::inflateInit();
9071
+ $inflate and ($v2, $stat) = $inflate->inflate($buff);
9072
+ if ($inflate and $stat == Compress::Zlib::Z_STREAM_END()) {
9073
+ $buff = $v2;
9074
+ my $len = length $buff;
9075
+ $et->VPrint(0, "$$et{INDENT}Inflated Item $id) '${type}' ($len bytes)\n");
9076
+ $et->VerboseDump(\$buff);
9077
+ } else {
9078
+ $warn = "Error inflating $name metadata";
9079
+ $et->WarnOnce($warn);
9080
+ $et->VPrint(0, "$$et{INDENT} [not extracted] ($warn)\n") if $verbose > 2;
9081
+ next;
9082
+ }
9083
+ }
9053
9084
  my ($start, $subTable, $proc);
9054
9085
  my $pos = $$item{Extents}[0][1] + $base;
9055
9086
  if ($name eq 'EXIF' and length $buff >= 4) {
@@ -9485,7 +9516,8 @@ sub ProcessMOV($$;$)
9485
9516
  my $dataPt = $$dirInfo{DataPt};
9486
9517
  my $verbose = $et->Options('Verbose');
9487
9518
  my $validate = $$et{OPTIONS}{Validate};
9488
- my $dataPos = $$dirInfo{Base} || 0;
9519
+ my $dirBase = $$dirInfo{Base} || 0;
9520
+ my $dataPos = $dirBase;
9489
9521
  my $dirID = $$dirInfo{DirID} || '';
9490
9522
  my $charsetQuickTime = $et->Options('CharsetQuickTime');
9491
9523
  my ($buff, $tag, $size, $track, $isUserData, %triplet, $doDefaultLang, $index);
@@ -9570,6 +9602,7 @@ sub ProcessMOV($$;$)
9570
9602
  $atomCount = $$tagTablePtr{VARS}{ATOM_COUNT};
9571
9603
  }
9572
9604
  my $lastTag = '';
9605
+ my $lastPos = 0;
9573
9606
  for (;;) {
9574
9607
  my ($eeTag, $ignore);
9575
9608
  last if defined $atomCount and --$atomCount < 0;
@@ -9608,6 +9641,8 @@ sub ProcessMOV($$;$)
9608
9641
  } elsif (not $et->Options('LargeFileSupport')) {
9609
9642
  $warnStr = 'End of processing at large atom (LargeFileSupport not enabled)';
9610
9643
  last;
9644
+ } elsif ($et->Options('LargeFileSupport') eq '2') {
9645
+ $et->WarnOnce('Processing large atom (LargeFileSupport is 2)');
9611
9646
  }
9612
9647
  }
9613
9648
  $size = $hi * 4294967296 + $lo - 16;
@@ -9702,7 +9737,7 @@ sub ProcessMOV($$;$)
9702
9737
  if ($size > 0x2000000) { # start to get worried above 32 MiB
9703
9738
  # check for RIFF trailer (written by Auto-Vox dashcam)
9704
9739
  if ($buff =~ /^(gpsa|gps0|gsen|gsea)...\0/s) { # (yet seen only gpsa as first record)
9705
- $et->VPrint(0, "Found RIFF trailer");
9740
+ $et->VPrint(0, sprintf("Found RIFF trailer at offset 0x%x",$lastPos));
9706
9741
  if ($et->Options('ExtractEmbedded')) {
9707
9742
  $raf->Seek(-8, 1) or last; # seek back to start of trailer
9708
9743
  my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
@@ -9711,6 +9746,11 @@ sub ProcessMOV($$;$)
9711
9746
  EEWarn($et);
9712
9747
  }
9713
9748
  last;
9749
+ } elsif ($buff eq 'CCCCCCCC') {
9750
+ $et->VPrint(0, sprintf("Found Kenwood trailer at offset 0x%x",$lastPos));
9751
+ my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
9752
+ ProcessKenwoodTrailer($et, { RAF => $raf }, $tbl);
9753
+ last;
9714
9754
  }
9715
9755
  $ignore = 1;
9716
9756
  if ($tagInfo and not $$tagInfo{Unknown} and not $eeTag) {
@@ -10082,14 +10122,15 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
10082
10122
  ) if $verbose;
10083
10123
  if ($size and (not $raf->Seek($size-1, 1) or $raf->Read($buff, 1) != 1)) {
10084
10124
  my $t = PrintableTagID($tag,2);
10085
- $warnStr = "Truncated '${t}' data";
10125
+ $warnStr = sprintf("Truncated '${t}' data at offset 0x%x", $lastPos);
10086
10126
  last;
10087
10127
  }
10088
10128
  }
10089
10129
  $dataPos += $size + 8; # point to start of next atom data
10090
10130
  last if $dirEnd and $dataPos >= $dirEnd; # (note: ignores last value if 0 bytes)
10131
+ $lastPos = $raf->Tell() + $dirBase;
10091
10132
  $raf->Read($buff, 8) == 8 or last;
10092
- $lastTag = $tag if $$tagTablePtr{$tag};
10133
+ $lastTag = $tag if $$tagTablePtr{$tag} and $tag ne 'free'; # (Insta360 sometimes puts free block before trailer)
10093
10134
  ($size, $tag) = unpack('Na4', $buff);
10094
10135
  ++$index if defined $index;
10095
10136
  }
@@ -10099,7 +10140,11 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
10099
10140
  if (($lastTag eq 'mdat' or $lastTag eq 'moov') and (not $$tagTablePtr{$tag} or
10100
10141
  ref $$tagTablePtr{$tag} eq 'HASH' and $$tagTablePtr{$tag}{Unknown}))
10101
10142
  {
10102
- $et->Warn('Unknown trailer with '.lcfirst($warnStr));
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
+ }
10103
10148
  } else {
10104
10149
  $et->Warn($warnStr);
10105
10150
  }
@@ -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 74 different formats of timed GPS metadata from video files.
112
+ currently reads 76 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' },
@@ -143,6 +143,7 @@ my %insvLimit = (
143
143
  ExposureCompensation => { PrintConv => 'Image::ExifTool::Exif::PrintFraction($val)', Groups => { 2 => 'Camera' } },
144
144
  ISO => { Groups => { 2 => 'Camera' } },
145
145
  CameraDateTime=>{ PrintConv => '$self->ConvertDateTime($val)', Groups => { 2 => 'Time' } },
146
+ DateTimeStamp =>{ PrintConv => '$self->ConvertDateTime($val)', Groups => { 2 => 'Time' } },
146
147
  VideoTimeStamp => { Groups => { 2 => 'Video' } },
147
148
  Accelerometer=> { Notes => '3-axis acceleration in units of g' },
148
149
  AccelerometerData => { },
@@ -992,8 +993,29 @@ sub Process_text($$$;$)
992
993
  $tags{Text} = defined $tags{Text} ? $tags{Text} . "\$$tag$dat" : "\$$tag$dat";
993
994
  }
994
995
  }
995
- %tags and HandleTextTags($et, $tagTbl, \%tags), return;
996
-
996
+ if (%tags) {
997
+ unless ($tags{Accelerometer}) { # (probably unnecessary test)
998
+ # check for NextBase 622GW accelerometer data
999
+ # Example data (leading 2-byte length word has been stripped by ProcessSamples):
1000
+ # 0000: 00 00 00 00 32 30 32 32 30 39 30 35 31 36 34 30 [....202209051640]
1001
+ # 0010: 33 33 00 00 29 00 ba ff 48 ff 18 00 f2 07 5a ff [33..)...H.....Z.]
1002
+ # 0020: 64 ff e8 ff 58 ff e8 ff c1 07 43 ff 41 ff d2 ff [d...X.....C.A...]
1003
+ # 0030: 58 ff ea ff dc 07 50 ff 30 ff e0 ff 72 ff d8 ff [X.....P.0...r...]
1004
+ # 0040: f5 07 51 ff 16 ff dc ff 6a ff ca ff 33 08 45 ff [..Q.....j...3.E.]
1005
+ if ($$dataPt =~ /^\0{4}(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\0\0.{2}/s) {
1006
+ $tags{DateTimeStamp} = "$1:$2:$2 $4:$5:$6";
1007
+ my $num = unpack('x20v', $$dataPt); # number of accelerometer readings
1008
+ if ($num and $num * 12 + 22 < length $$dataPt) {
1009
+ $num *= 6;
1010
+ my @acc = unpack("x22v$num", $$dataPt);
1011
+ map { $_ = $_ - 0x10000 if $_ >= 0x8000 } @acc;
1012
+ $tags{AccelerometerData} = "@acc";
1013
+ }
1014
+ }
1015
+ }
1016
+ HandleTextTags($et, $tagTbl, \%tags);
1017
+ return;
1018
+ }
997
1019
  # check for enciphered binary GPS data
998
1020
  # BlueSkySea:
999
1021
  # 0000: 00 00 aa aa aa aa 54 54 98 9a 9b 93 9a 92 98 9a [......TT........]
@@ -1398,9 +1420,10 @@ Sample: for ($i=0; ; ) {
1398
1420
  } elsif ($type eq 'gps ') { # (ie. GPSDataList tag)
1399
1421
 
1400
1422
  if ($buff =~ /^....freeGPS /s) {
1401
- # process by brute scan instead if ExtractEmbedded >= 3
1402
- # (some videos don't reference all freeGPS info from 'gps ' table, eg. INNOV)
1403
- last if $eeOpt >= 3;
1423
+ # parse freeGPS data unless done already in brute-force scan
1424
+ # (some videos don't reference all freeGPS info from 'gps ' table, eg. INNOV,
1425
+ # and some videos don't put 'gps ' data in mdat, eg XGODY 12" 4K Dashcam)
1426
+ last if $$et{FoundGPSByScan};
1404
1427
  # decode "freeGPS " data (Novatek and others)
1405
1428
  ProcessFreeGPS($et, {
1406
1429
  DataPt => \$buff,
@@ -2027,9 +2050,41 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2027
2050
  }
2028
2051
  }
2029
2052
 
2030
- } else {
2053
+ } elsif ($$dataPt =~ m<^.{23}(\d{4})/(\d{2})/(\d{2}) (\d{2}):(\d{2}):(\d{2}) [N|S]>s) {
2031
2054
 
2032
2055
  $debug and $et->FoundTag(GPSType => 16);
2056
+ # XGODY 12" 4K Dashcam
2057
+ # 0000: 00 00 00 a8 66 72 65 65 47 50 53 20 98 00 00 00 [....freeGPS ....]
2058
+ # 0010: 6e 6f 72 6d 61 6c 3a 32 30 32 34 2f 30 35 2f 32 [normal:2024/05/2]
2059
+ # 0020: 32 20 30 32 3a 35 34 3a 32 39 20 4e 3a 34 32 2e [2 02:54:29 N:42.]
2060
+ # 0030: 33 38 32 34 37 30 20 57 3a 38 33 2e 33 38 39 35 [382470 W:83.3895]
2061
+ # 0040: 37 30 20 35 33 2e 36 20 6b 6d 2f 68 20 78 3a 2d [70 53.6 km/h x:-]
2062
+ # 0050: 30 2e 30 32 20 79 3a 30 2e 39 39 20 7a 3a 30 2e [0.02 y:0.99 z:0.]
2063
+ # 0060: 31 30 20 41 3a 32 36 39 2e 32 20 48 3a 32 34 35 [10 A:269.2 H:245]
2064
+ # 0070: 2e 35 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [.5..............]
2065
+ ($yr,$mon,$day,$hr,$min,$sec) = ($1,$2,$3,$4,$5,$6);
2066
+ $$dataPt =~ s/\0+$//; # remove trailing nulls
2067
+ my @a = split ' ', substr($$dataPt,43);
2068
+ $ddd = 1;
2069
+ foreach (@a) {
2070
+ unless (/^([A-Z]):([-+]?\d+(\.\d+)?)$/i) {
2071
+ # (the "km/h" after spd is display units? because the value is stored in knots)
2072
+ defined $lon and not defined $spd and /^\d+\.\d+$/ and $spd = $_ * $knotsToKph;
2073
+ next;
2074
+ }
2075
+ ($1 eq 'N' or $1 eq 'S') and $lat = $2, $latRef = $1, next;
2076
+ ($1 eq 'E' or $1 eq 'W') and $lon = $2, $lonRef = $1, next;
2077
+ ($1 eq 'x' or $1 eq 'y' or $1 eq 'z') and push(@acc,$2), next;
2078
+ $1 eq 'A' and $trk = $2, next; # (verified, but why 'A'?)
2079
+ # seen 'H' - one might expect altitude ('H'eight), but it doesn't fit
2080
+ # the sample data, so save all other information as an "Unknown_X" tag
2081
+ $$tagTbl{$1} or AddTagToTable($tagTbl, $1, { Name => "Unknown_$1", Unknown => 1 });
2082
+ push(@xtra, $1 => $2), next;
2083
+ }
2084
+
2085
+ } else {
2086
+
2087
+ $debug and $et->FoundTag(GPSType => 17);
2033
2088
  # (look for binary GPS as stored by Nextbase 512G, ref PH)
2034
2089
  # 0000: 00 00 80 00 66 72 65 65 47 50 53 20 78 01 00 00 [....freeGPS x...]
2035
2090
  # 0010: 78 2e 78 78 00 00 00 00 00 00 00 00 00 00 00 00 [x.xx............]
@@ -2093,7 +2148,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2093
2148
  my $time = sprintf('%.2d:%.2d:%sZ',$hr,$min,$sec);
2094
2149
  $et->HandleTag($tagTbl, GPSTimeStamp => $time);
2095
2150
  }
2096
- if (defined $lat) {
2151
+ if (defined $lat and defined $lon) {
2097
2152
  # lat/long are in DDDMM.MMMM format unless $ddd is set
2098
2153
  ConvertLatLon($lat, $lon) unless $ddd;
2099
2154
  $et->HandleTag($tagTbl, GPSLatitude => $lat * ($latRef eq 'S' ? -1 : 1));
@@ -2658,6 +2713,53 @@ sub ProcessRIFFTrailer($$$)
2658
2713
  return 1;
2659
2714
  }
2660
2715
 
2716
+ #------------------------------------------------------------------------------
2717
+ # Process Kenwood Dashcam trailer (forum16229)
2718
+ # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
2719
+ # Returns: 1 on success
2720
+ # Sample data (chained 512-byte records starting like this):
2721
+ # 0000: 43 43 43 43 43 43 43 43 43 43 43 43 43 43 47 50 [CCCCCCCCCCCCCCGP]
2722
+ # 0010: 53 44 41 54 41 2d 2d 32 30 32 34 30 37 31 31 31 [SDATA--202407111]
2723
+ # 0020: 32 30 34 31 32 4e 35 30 2e 36 31 32 33 38 36 30 [20412N50.6123860]
2724
+ # 0030: 36 37 37 45 38 2e 37 30 32 37 31 38 30 39 38 39 [677E8.7027180989]
2725
+ # 0040: 35 33 33 2e 30 30 30 30 30 30 30 30 30 30 30 30 [533.000000000000]
2726
+ # 0050: 2e 30 30 30 30 30 30 30 30 30 30 30 30 30 2e 30 [.0000000000000.0]
2727
+ # 0060: 31 39 39 39 39 39 39 39 35 35 33 2d 30 2e 30 39 [19999999553-0.09]
2728
+ # 0070: 30 30 30 30 30 30 33 35 37 2d 30 2e 31 34 30 30 [000000357-0.1400]
2729
+ # 0080: 30 30 30 30 30 35 39 47 50 53 44 41 54 41 2d 2d [0000059GPSDATA--]
2730
+ sub ProcessKenwoodTrailer($$$)
2731
+ {
2732
+ my ($et, $dirInfo, $tagTbl) = @_;
2733
+ my $raf = $$dirInfo{RAF};
2734
+ my $buff;
2735
+ # current file position is 8 bytes into the 14 C's, so test the next 6:
2736
+ $raf->Read($buff, 14) and $buff eq 'CCCCCCCCCCCCCC' or return 0;
2737
+ $et->VerboseDir('Kenwood trailer', undef, undef);
2738
+ unless ($$et{OPTIONS}{ExtractEmbedded}) {
2739
+ $et->WarnOnce('Use the ExtractEmbedded option to extract timed GPSData from Kenwood trailer',3);
2740
+ return 1;
2741
+ }
2742
+ while ($raf->Read($buff, 121) and $buff =~ /^GPSDATA--(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/) {
2743
+ FoundSomething($et, $tagTbl);
2744
+ $et->HandleTag($tagTbl, GPSDateTime => "$1:$2:$3 $4:$5:$6");
2745
+ my $i = 9 + 14;
2746
+ my ($val, @acc, $tag);
2747
+ foreach $tag (qw(GPSLatitude GPSLongitude GPSSpeed unk acc acc acc)) {
2748
+ $val = substr($buff, $i, 14); $i += 14;
2749
+ next if $tag eq 'unk';
2750
+ my $hemi;
2751
+ $hemi = $1 if $val =~ s/^([NSEW])//;
2752
+ $val =~ /^[-+]?\d+\.\d+$/ or next;
2753
+ $tag eq 'acc' and push(@acc,$val), next;
2754
+ $val = -$val if $hemi and ($hemi eq 'S' or $hemi eq 'W');
2755
+ $et->HandleTag($tagTbl, $tag => $val);
2756
+ }
2757
+ $et->HandleTag($tagTbl, Accelerometer => "@acc") if @acc == 3;
2758
+ }
2759
+ delete $$et{DOC_NUM};
2760
+ return 1;
2761
+ }
2762
+
2661
2763
  #------------------------------------------------------------------------------
2662
2764
  # Process 'gps ' atom containing NMEA from Pittasoft Blackvue dashcam (ref PH)
2663
2765
  # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
@@ -3331,6 +3433,7 @@ sub ScanMediaData($)
3331
3433
  }
3332
3434
  my $dirInfo = { DataPt => \$buff, DataPos => $pos + $dataPos, DirLen => $len };
3333
3435
  ProcessFreeGPS($et, $dirInfo, $tagTbl);
3436
+ $$et{FoundGPSByScan} = 1;
3334
3437
  }
3335
3438
  $pos += $len;
3336
3439
  $buf2 = substr($buff, $len);
@@ -30,7 +30,7 @@ use strict;
30
30
  use vars qw($VERSION $AUTOLOAD);
31
31
  use Image::ExifTool qw(:DataAccess :Utils);
32
32
 
33
- $VERSION = '1.67';
33
+ $VERSION = '1.68';
34
34
 
35
35
  sub ConvertTimecode($);
36
36
  sub ProcessSGLT($$$);
@@ -2041,11 +2041,16 @@ sub ProcessRIFF($$)
2041
2041
  last unless $moviEnd;
2042
2042
  # we arrived here because there was a problem parsing the movie data
2043
2043
  # so seek to the end to continue processing
2044
- if ($moviEnd > 0x7fffffff and not $et->Options('LargeFileSupport')) {
2045
- $et->Warn('Possibly corrupt LIST_movi data');
2046
- $et->Warn('Stopped parsing at large LIST_movi chunk (LargeFileSupport not set)');
2047
- undef $err;
2048
- last;
2044
+ if ($moviEnd > 0x7fffffff) {
2045
+ unless ($et->Options('LargeFileSupport')) {
2046
+ $et->Warn('Possibly corrupt LIST_movi data');
2047
+ $et->Warn('Stopped parsing at large LIST_movi chunk (LargeFileSupport not set)');
2048
+ undef $err;
2049
+ last;
2050
+ }
2051
+ if ($et->Options('LargeFileSupport') eq '2') {
2052
+ $et->WarnOnce('Processing large chunk (LargeFileSupport is 2)');
2053
+ }
2049
2054
  }
2050
2055
  if ($validate) {
2051
2056
  # (must actually try to read something after seeking to detect error)
@@ -2159,10 +2164,15 @@ sub ProcessRIFF($$)
2159
2164
  $moviEnd = $raf->Tell() + $len2;
2160
2165
  next; # parse into movi chunk
2161
2166
  } elsif (not $rewind) {
2162
- if ($len > 0x7fffffff and not $et->Options('LargeFileSupport')) {
2163
- $tag =~ s/([\0-\x1f\x7f-\xff])/sprintf('\\x%.2x',ord $1)/eg;
2164
- $et->Warn("Stopped parsing at large $tag chunk (LargeFileSupport not set)");
2165
- last;
2167
+ if ($len > 0x7fffffff) {
2168
+ unless ($et->Options('LargeFileSupport')) {
2169
+ $tag =~ s/([\0-\x1f\x7f-\xff])/sprintf('\\x%.2x',ord $1)/eg;
2170
+ $et->Warn("Stopped parsing at large $tag chunk (LargeFileSupport not set)");
2171
+ last;
2172
+ }
2173
+ if ($et->Options('LargeFileSupport') eq '2') {
2174
+ $et->WarnOnce('Processing large chunk (LargeFileSupport is 2)');
2175
+ }
2166
2176
  }
2167
2177
  if ($validate and $len2) {
2168
2178
  # (must actually try to read something after seeking to detect error)
@@ -22,13 +22,13 @@ use vars qw($VERSION %samsungLensTypes);
22
22
  use Image::ExifTool qw(:DataAccess :Utils);
23
23
  use Image::ExifTool::Exif;
24
24
 
25
- $VERSION = '1.56';
25
+ $VERSION = '1.57';
26
26
 
27
27
  sub WriteSTMN($$$);
28
28
  sub ProcessINFO($$$);
29
29
  sub ProcessSamsungMeta($$$);
30
30
  sub ProcessSamsungIFD($$$);
31
- sub ProcessSamsung($$$);
31
+ sub ProcessSamsung($$;$);
32
32
 
33
33
  # Samsung LensType lookup
34
34
  %samsungLensTypes = (
@@ -943,25 +943,25 @@ my %formatMinMax = (
943
943
  );
944
944
 
945
945
  # information extracted from Samsung trailer (ie. Samsung SM-T805 "Sound & Shot" JPEG) (ref PH)
946
+ # NOTE: These tags may use $$self{SamsungTagName} in a Condition statement
947
+ # if necessary to differentiate tags with the same ID but different names
946
948
  %Image::ExifTool::Samsung::Trailer = (
947
949
  GROUPS => { 0 => 'MakerNotes', 2 => 'Other' },
948
950
  VARS => { NO_ID => 1, HEX_ID => 0 },
949
951
  PROCESS_PROC => \&ProcessSamsung,
952
+ TAG_PREFIX => 'SamsungTrailer',
950
953
  PRIORITY => 0, # (first one takes priority so DepthMapWidth/Height match first DepthMapData)
951
954
  NOTES => q{
952
- Tags extracted from the trailer of JPEG images written when using certain
953
- features (such as "Sound & Shot" or "Shot & More") from Samsung models such
954
- as the Galaxy S4 and Tab S, and from the 'sefd' atom in HEIC images from the
955
- Samsung S10+.
956
- },
957
- '0x0001-name' => {
958
- Name => 'EmbeddedImageName', # ("DualShot_1","DualShot_2")
959
- RawConv => '$$self{EmbeddedImageName} = $val',
955
+ Tags extracted from the SEFT trailer of JPEG and PNG images written when
956
+ using certain features (such as "Sound & Shot" or "Shot & More") from
957
+ Samsung models such as the Galaxy S4 and Tab S, and from the 'sefd' atom in
958
+ HEIC images from models such as the S10+.
960
959
  },
960
+ '0x0001-name' => 'EmbeddedImageName', # ("DualShot_1","DualShot_2")
961
961
  '0x0001' => [
962
962
  {
963
963
  Name => 'EmbeddedImage',
964
- Condition => '$$self{EmbeddedImageName} eq "DualShot_1"',
964
+ Condition => '$$self{SamsungTagName} ne "DualShot_2"',
965
965
  Groups => { 2 => 'Preview' },
966
966
  Binary => 1,
967
967
  },
@@ -979,6 +979,7 @@ my %formatMinMax = (
979
979
  # 0x0800 - unknown (29 bytes) (contains already-extracted EmbeddedAudioFileName)
980
980
  # 0x0830-name - seen '1165724808.pre'
981
981
  # 0x0830 - unknown (164004 bytes)
982
+ # 0x08c0-name - seen 'Auto_Enhance_Info' #forum16086
982
983
  # 0x08d0-name - seen 'Interactive_Panorama_Info'
983
984
  # 0x08d0 - unknown (7984 bytes)
984
985
  # 0x08e0-name - seen 'Panorama_Shot_Info'
@@ -988,6 +989,8 @@ my %formatMinMax = (
988
989
  # 0x0910 - string, seen 'Front_Cam_Selfie_Info'
989
990
  # 0x09e0-name - seen 'Burst_Shot_Info'
990
991
  # 0x09e0 - string, seen '489489125'
992
+ # 0x09e1-name - seen 'BurstShot_Best_Photo_Info' #forum16086
993
+ # 0x09f0-name - seen 'Pro_Mode_Info' #forum16086
991
994
  # 0x0a01-name - seen 'Image_UTC_Data'
992
995
  '0x0a01' => { #forum7161
993
996
  Name => 'TimeStamp',
@@ -999,6 +1002,7 @@ my %formatMinMax = (
999
1002
  '0x0a20' => { Name => 'DualCameraImage', Groups => { 2 => 'Preview' }, Binary => 1 },
1000
1003
  '0x0a30-name' => 'EmbeddedVideoType', # ("MotionPhoto_Data")
1001
1004
  '0x0a30' => { Name => 'EmbeddedVideoFile', Groups => { 2 => 'Video' }, Binary => 1 }, #forum7161
1005
+ # 0x0a41-name - seen 'BackupRestore_Data' #forum16086
1002
1006
  # 0x0aa1-name - seen 'MCC_Data'
1003
1007
  # 0x0aa1 - seen '204','222','234','302','429'
1004
1008
  '0x0aa1' => {
@@ -1254,15 +1258,45 @@ my %formatMinMax = (
1254
1258
  Name => 'DualShotExtra',
1255
1259
  SubDirectory => { TagTable => 'Image::ExifTool::Samsung::DualShotExtra' },
1256
1260
  },
1261
+ # 0x0ab4-name - seen 'DualShot_Core_Info' #forum16086
1257
1262
  # 0x0ac0-name - seen 'ZoomInOut_Info' (SM-N950U)
1258
1263
  # 0x0ac0 - 2048 bytes of interesting stuff including firmware version? (SM-N950U)
1264
+ # 0x0b30-name - seen 'Camera_Sticker_Info' #forum16086
1259
1265
  '0x0b40' => { # (SM-N975X front camera)
1260
1266
  Name => 'SingleShotMeta',
1261
1267
  SubDirectory => { TagTable => 'Image::ExifTool::Samsung::SingleShotMeta' },
1262
1268
  },
1263
1269
  # 0x0b41-name - seen 'SingeShot_DepthMap_1' (Yes, "Singe") (SM-N975X front camera)
1264
1270
  '0x0b41' => { Name => 'SingleShotDepthMap', Binary => 1 },
1265
- # 0x0ba1-name - seen 'Original_Path_Hash_Key', 'PhotoEditor_Re_Edit_Data'
1271
+ # 0x0b51-name - seen 'Intelligent_PhotoEditor_Data' #forum16086
1272
+ # 0x0b60-name - seen 'UltraWide_PhotoEditor_Data' #forum16086
1273
+ # 0x0b90-name - seen 'Document_Scan_Info' #forum16086
1274
+ # 0x0ba1-name - seen 'Original_Path_Hash_Key', 'PhotoEditor_Re_Edit_Data', 'deco_doodle_bitmap', 'deco_sticker_bitmap', 'deco_text_bitmap'
1275
+ # 0x0ba2-name - seen 'Copy_Available_Edit_Info' #forum16086
1276
+ # 0x0bc0-name - seen 'Single_Relighting_Bokeh_Info' #forum16086
1277
+ # 0x0bd0-name - seen 'Dual_Relighting_Bokeh_Info' #forum16086
1278
+ # 0x0be0-name - seen 'Livefocus_JDM_Info' #forum16086
1279
+ # 0x0bf0-name - seen 'Remaster_Info' #forum16086
1280
+ '0x0bf0' => 'RemasterInfo', #forum16086/16242
1281
+ # 0x0c21-name - seen 'Portrait_Effect_Info' #forum16086
1282
+ # 0x0c51-name - seen 'Samsung_Capture_Info' #forum16086
1283
+ '0x0c51' => 'SamsungCaptureInfo', #forum16086/16242
1284
+ # 0x0c61-name - seen 'Camera_Capture_Mode_Info' #forum16086
1285
+ # 0x0c71-name - seen 'Pro_White_Balance_Info' #forum16086
1286
+ # 0x0c81-name - seen 'Watermark_Info' #forum16086
1287
+ # 0x0cc1-name - seen 'Color_Display_P3' #forum16086
1288
+ # 0x0cd2-name - seen 'Photo_HDR_Info' #forum16086
1289
+ # 0x0ce1-name - seen 'Gallery_DC_Data' #forum16086
1290
+ # 0x0d01-name - seen 'Camera_Scene_Info', 'Camera_Scene_Info2', 'Camera_Scene_Info3' #forum16086
1291
+ # 0x0d11-name - seen 'Video_Snapshot_Info' #forum16086
1292
+ # 0x0d21-name - seen 'Camera_Scene_Info' #forum16086
1293
+ # 0x0d31-name - seen 'Food_Blur_Effect_Info' #forum16086
1294
+ '0x0d91' => { #forum16086/16242
1295
+ Name => 'PEg_Info',
1296
+ Description => 'PEg Info',
1297
+ SubDirectory => { TagTable => 'Image::ExifTool::JSON::Main' },
1298
+ },
1299
+ # 0x0da1-name - seen 'Captured_App_Info' #forum16086
1266
1300
  # 0xa050-name - seen 'Jpeg360_2D_Info' (Samsung Gear 360)
1267
1301
  # 0xa050 - seen 'Jpeg3602D' (Samsung Gear 360)
1268
1302
  # 0x0c81-name - seen 'Watermark_Info'
@@ -1535,7 +1569,7 @@ sub ProcessSamsungIFD($$$)
1535
1569
  # Returns: 1 on success, 0 not valid Samsung trailer, or -1 error writing
1536
1570
  # - updates DataPos to point to start of Samsung trailer
1537
1571
  # - updates DirLen to existing trailer length
1538
- sub ProcessSamsung($$$)
1572
+ sub ProcessSamsung($$;$)
1539
1573
  {
1540
1574
  my ($et, $dirInfo) = @_;
1541
1575
  my $raf = $$dirInfo{RAF};
@@ -1625,8 +1659,13 @@ SamBlock:
1625
1659
  $audioSize = $size - 8 - $len;
1626
1660
  next;
1627
1661
  }
1628
- # add unknown tags if necessary
1662
+ last unless $raf->Seek($dirPos-$noff, 0) and $raf->Read($buf2, $size) == $size;
1663
+ # (could validate the first 4 bytes of the block because they
1664
+ # are the same as the first 4 bytes of the directory entry)
1665
+ $len = Get32u(\$buf2, 4);
1666
+ last if $len + 8 > $size;
1629
1667
  my $tag = sprintf("0x%.4x", $type);
1668
+ # add unknown tags if necessary
1630
1669
  unless ($$tagTablePtr{$tag}) {
1631
1670
  next unless $unknown or $verbose;
1632
1671
  my %tagInfo = (
@@ -1645,11 +1684,8 @@ SamBlock:
1645
1684
  );
1646
1685
  AddTagToTable($tagTablePtr, "$tag-name", \%tagInfo2);
1647
1686
  }
1648
- last unless $raf->Seek($dirPos-$noff, 0) and $raf->Read($buf2, $size) == $size;
1649
- # (could validate the first 4 bytes of the block because they
1650
- # are the same as the first 4 bytes of the directory entry)
1651
- $len = Get32u(\$buf2, 4);
1652
- last if $len + 8 > $size;
1687
+ # set SamsungTagName ExifTool member for use in tag Condition
1688
+ $$et{SamsungTagName} = substr($buf2, 8, $len);
1653
1689
  # extract tag name and value
1654
1690
  $et->HandleTag($tagTablePtr, "$tag-name", undef,
1655
1691
  DataPt => \$buf2,
@@ -1663,6 +1699,7 @@ SamBlock:
1663
1699
  Start => 8 + $len,
1664
1700
  Size => $size - (8 + $len),
1665
1701
  );
1702
+ delete $$et{SamsungTagName};
1666
1703
  }
1667
1704
  if ($outfile) {
1668
1705
  last unless $raf->Seek($dataPos, 0) and $raf->Read($buff, $dirLen) == $dirLen;