exiftool_vendored 12.70.0 → 12.72.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -83,7 +83,8 @@ my %processByMetaFormat = (
83
83
 
84
84
  # data lengths for each INSV/INSP record type
85
85
  my %insvDataLen = (
86
- 0x200 => 0, # PreivewImage (any size) (a duplicate of PreviewImage in APP2 of INSP files)
86
+ 0x000 => 0, # directory table (any size)
87
+ 0x200 => 0, # PreviewImage (any size) (a duplicate of PreviewImage in APP2 of INSP files)
87
88
  0x300 => 0, # accelerometer (could be either 20 or 56 bytes)
88
89
  0x400 => 16, # exposure (ref 6)
89
90
  0x600 => 8, # timestamps (ref 6)
@@ -91,6 +92,8 @@ my %insvDataLen = (
91
92
  # 0x900 => 48, # ? (Insta360 X3)
92
93
  # 0xa00 => 5?, # ? (Insta360 ONE RS)
93
94
  # 0xb00 => 10, # ? (Insta360 X3)
95
+ # 0xd00 => 10, # ? (Insta360 Ace Pro)
96
+ # 0x1200 ? # ? (Insta360 Ace Pro)
94
97
  );
95
98
 
96
99
  # limit the default amount of data we read for some record types
@@ -106,7 +109,7 @@ my %insvLimit = (
106
109
  The tags below are extracted from timed metadata in QuickTime and other
107
110
  formats of video files when the ExtractEmbedded option is used. Although
108
111
  most of these tags are combined into the single table below, ExifTool
109
- currently reads 66 different formats of timed GPS metadata from video files.
112
+ currently reads 67 different formats of timed GPS metadata from video files.
110
113
  },
111
114
  VARS => { NO_ID => 1 },
112
115
  GPSLatitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', RawConv => '$$self{FoundGPSLatitude} = 1; $val' },
@@ -2043,7 +2046,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2043
2046
  return 1;
2044
2047
 
2045
2048
  } elsif ($$dataPt =~ /^.{28}A.{11}([NS]).{15}([EW])/s) {
2046
-
2049
+
2047
2050
  $debug and $et->FoundTag(GPSType => '2G');
2048
2051
  # Vantrue N4 dashcam
2049
2052
  # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
@@ -2778,7 +2781,7 @@ sub ProcessInsta360($;$)
2778
2781
  my ($et, $dirInfo) = @_;
2779
2782
  my $raf = $$et{RAF};
2780
2783
  my $offset = $dirInfo ? $$dirInfo{Offset} || 0 : 0;
2781
- my $buff;
2784
+ my ($buff, $dirTable, $dirTablePos);
2782
2785
 
2783
2786
  return 0 unless $raf->Seek(-78-$offset, 2) and $raf->Read($buff, 78) == 78 and
2784
2787
  substr($buff,-32) eq "8db42d694ccc418790edff439fe026bf"; # check magic number
@@ -2868,7 +2871,19 @@ sub ProcessInsta360($;$)
2868
2871
  if ($len % $dlen and $id != 0x700) { # (have seen one 0x700 record which was expected format but not multiple of 53 bytes)
2869
2872
  $et->Warn(sprintf('Unexpected Insta360 record 0x%x length',$id));
2870
2873
  } elsif ($id == 0x200) {
2871
- $et->FoundTag(PreviewImage => $buff);
2874
+ # there are 4 types of record 0x200
2875
+ # 1. JPEG preview (starts with ff d8 ff e1)
2876
+ # 2. TIFF preview (starts with 01 00 00 00, then record length)
2877
+ # 3. Unknown 1 (starts with 00 00 00 01)
2878
+ # 4. Unknown 2 (starts with 00 00 01 34)
2879
+ if ($buff =~ /^\xff\xd8\xff/) {
2880
+ $et->FoundTag(PreviewImage => $buff);
2881
+ } elsif ($buff =~ /^\x01\0\0\0(.{4})\x01/s and unpack('V',$1) == $dlen) {
2882
+ my ($w, $h) = unpack('x16V2',$buff);
2883
+ # build the TIFF image (could the 1 at byte 9 be the SamplesPerPixel?)
2884
+ my $hdr = Image::ExifTool::MakeTiffHeader($w, $h, 1, 8);
2885
+ $et->FoundTag(PreviewTIFF => $hdr . substr($buff, 40));
2886
+ }
2872
2887
  } elsif ($id == 0x300) {
2873
2888
  for ($p=0; $p<$len; $p+=$dlen) {
2874
2889
  $$et{DOC_NUM} = ++$$et{DOC_COUNT};
@@ -2899,7 +2914,7 @@ sub ProcessInsta360($;$)
2899
2914
  my $tmp = substr($buff, $p, $dlen);
2900
2915
  my @a = unpack('VVvaa8aa8aa8a8a8', $tmp);
2901
2916
  unless (($a[5] eq 'N' or $a[5] eq 'S') and # (quick validation)
2902
- ($a[7] eq 'E' or $a[7] eq 'W' or
2917
+ ($a[7] eq 'E' or $a[7] eq 'W' or
2903
2918
  # (odd, but I've seen "O" instead of "W". Perhaps
2904
2919
  # when the language is french? ie. "Ouest"?)
2905
2920
  $a[7] eq 'O'))
@@ -2913,13 +2928,15 @@ sub ProcessInsta360($;$)
2913
2928
  $a[$_] = GetDouble(\$a[$_], 0) foreach 4,6,8,9,10;
2914
2929
  $a[4] = -abs($a[4]) if $a[5] eq 'S'; # (abs just in case it was already signed)
2915
2930
  $a[6] = -abs($a[6]) if $a[7] ne 'E';
2916
- $et->HandleTag($tagTbl, GPSDateTime => Image::ExifTool::ConvertUnixTime($a[0]) . 'Z');
2931
+ my $ms = '';
2932
+ $a[2] and ($ms = sprintf('.%.3d', $a[2])) =~ s/0+$//;
2933
+ $et->HandleTag($tagTbl, GPSDateTime => Image::ExifTool::ConvertUnixTime($a[0]) . $ms . 'Z');
2917
2934
  $et->HandleTag($tagTbl, GPSLatitude => $a[4]);
2918
2935
  $et->HandleTag($tagTbl, GPSLongitude => $a[6]);
2919
2936
  $et->HandleTag($tagTbl, GPSSpeed => $a[8] * $mpsToKph);
2920
2937
  $et->HandleTag($tagTbl, GPSTrack => $a[9]);
2921
2938
  $et->HandleTag($tagTbl, GPSAltitude => $a[10]);
2922
- $et->HandleTag($tagTbl, Unknown02 => "@a[1,2]") if $unknown; # millisecond counter (https://exiftool.org/forum/index.php?topic=9884.msg65143#msg65143)
2939
+ $et->HandleTag($tagTbl, Unknown02 => $a[1]) if $unknown;
2923
2940
  }
2924
2941
  }
2925
2942
  } elsif ($id == 0x101) {
@@ -2932,10 +2949,41 @@ sub ProcessInsta360($;$)
2932
2949
  $et->HandleTag($tagTablePtr, $t, $val);
2933
2950
  $p += 2 + $n;
2934
2951
  }
2952
+ } elsif ($id == 0x0) {
2953
+ last if not $len;
2954
+ # example directory table for record locations from Insta360AcePro MP4 video:
2955
+ # vv vv - record ID
2956
+ # vv vv vv vv - record size
2957
+ # vv vv vv vv - offset from start of footer
2958
+ # 00 00 00 00 00 00 00 00 00 00
2959
+ # 01 01 82 04 00 00 1b 45 62 00
2960
+ # 02 00 28 46 05 00 ed fe 5c 00
2961
+ # 03 00 40 aa 24 00 ed fe 34 00
2962
+ # 04 00 00 c1 01 00 ed fe 30 00
2963
+ # [...]
2964
+ unless ($dirTable) {
2965
+ $dirTable = $buff;
2966
+ $dirTablePos = 0;
2967
+ }
2935
2968
  }
2936
- ($epos -= 6) + $trailerLen < 0 and last; # step back to previous record
2937
- $raf->Seek($epos, 2) or last;
2938
- $raf->Read($buff, 6) == 6 or last;
2969
+ # step through directory table instead of sequential scanning if possible
2970
+ if ($dirTable) {
2971
+ undef $epos;
2972
+ for (;;) {
2973
+ last if $dirTablePos + 10 > length($dirTable);
2974
+ my ($id, $siz, $off) = unpack("x${dirTablePos}vVV", $dirTable);
2975
+ $dirTablePos += 10;
2976
+ if ($id and $siz and $off + $siz < $trailerLen) {
2977
+ $epos = $off + $siz - $trailerLen;
2978
+ last;
2979
+ }
2980
+ }
2981
+ last unless defined $epos;
2982
+ } else {
2983
+ ($epos -= 6) + $trailerLen < 0 and last; # step back to previous record
2984
+ }
2985
+ $raf->Seek($epos, 2) or last; # seek to start of next footer
2986
+ $raf->Read($buff, 6) == 6 or last; # read footer
2939
2987
  }
2940
2988
  $$et{DOC_NUM} = 0;
2941
2989
  SetByteOrder('MM');
@@ -9,6 +9,7 @@
9
9
  # 2) http://homepage3.nifty.com/kamisaka/makernote/makernote_ricoh.htm
10
10
  # 3) Tim Gray private communication (GR)
11
11
  # 4) https://github.com/atotto/ricoh-theta-tools/
12
+ # 5) https://github.com/ricohapi/theta-api-specs/blob/main/theta-metadata/README.md
12
13
  # IB) Iliah Borg private communication (LibRaw)
13
14
  #------------------------------------------------------------------------------
14
15
 
@@ -18,11 +19,13 @@ use strict;
18
19
  use vars qw($VERSION);
19
20
  use Image::ExifTool qw(:DataAccess :Utils);
20
21
  use Image::ExifTool::Exif;
22
+ use Image::ExifTool::GPS;
21
23
 
22
- $VERSION = '1.36';
24
+ $VERSION = '1.37';
23
25
 
24
26
  sub ProcessRicohText($$$);
25
27
  sub ProcessRicohRMETA($$$);
28
+ sub ProcessRicohRDT($$$);
26
29
 
27
30
  # lens types for Ricoh GXR
28
31
  my %ricohLensIDs = (
@@ -908,6 +911,65 @@ my %ricohLensIDs = (
908
911
  },
909
912
  );
910
913
 
914
+ # real-time metadata in RDTA atom (ref 5)
915
+ %Image::ExifTool::Ricoh::RDTA = (
916
+ PROCESS_PROC => \&ProcessRicohRDT,
917
+ GROUPS => { 0 => 'MakerNotes', 2 => 'Location' },
918
+ 0 => { Name => 'Accelerometer', Format => 'float[3]' },
919
+ 16 => { Name => 'TimeStamp', Format => 'int64u', ValueConv => '$val * 1e-9' },
920
+ );
921
+
922
+ # real-time metadata in RDTB atom (ref 5)
923
+ %Image::ExifTool::Ricoh::RDTB = (
924
+ PROCESS_PROC => \&ProcessRicohRDT,
925
+ GROUPS => { 0 => 'MakerNotes', 2 => 'Location' },
926
+ 0 => { Name => 'Gyroscope', Format => 'float[3]', Notes => 'rad/s' },
927
+ 16 => { Name => 'TimeStamp', Format => 'int64u', ValueConv => '$val * 1e-9' },
928
+ );
929
+
930
+ # real-time metadata in RDTC atom (ref 5)
931
+ %Image::ExifTool::Ricoh::RDTC = (
932
+ PROCESS_PROC => \&ProcessRicohRDT,
933
+ GROUPS => { 0 => 'MakerNotes', 2 => 'Location' },
934
+ 0 => { Name => 'MagneticField', Format => 'float[3]' },
935
+ 16 => { Name => 'TimeStamp', Format => 'int64u', ValueConv => '$val * 1e-9' },
936
+ );
937
+
938
+ # real-time metadata in RDTG atom (ref 5)
939
+ %Image::ExifTool::Ricoh::RDTG = (
940
+ PROCESS_PROC => \&ProcessRicohRDT,
941
+ GROUPS => { 0 => 'MakerNotes', 2 => 'Video' },
942
+ 0 => { Name => 'TimeStamp', Format => 'int64u', ValueConv => '$val * 1e-9' },
943
+ 100 => { Name => 'FrameNumber', Notes => 'generated internally' },
944
+ );
945
+
946
+ # real-time metadata in RDTL atom (ref 5)
947
+ %Image::ExifTool::Ricoh::RDTL = (
948
+ GROUPS => { 0 => 'MakerNotes', 2 => 'Location' },
949
+ 0 => {
950
+ Name => 'GPSDateTime',
951
+ Groups => { 2 => 'Time' },
952
+ Format => 'double',
953
+ ValueConv => 'ConvertUnixTime($val*1e-9, 1, 9)', # (NC -- what is the epoch?)
954
+ PrintConv => '$self->ConvertDateTime($val)',
955
+ },
956
+ 8 => {
957
+ Name => 'GPSLatitude',
958
+ Format => 'double',
959
+ PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1)',
960
+ },
961
+ 16 => {
962
+ Name => 'GPSLongitude',
963
+ Format => 'double',
964
+ PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1)',
965
+ },
966
+ 24 => {
967
+ Name => 'GPSAltitude',
968
+ Format => 'double',
969
+ PrintConv => '($val =~ s/^-// ? "$val m Below" : "$val m Above") . " Sea Level"',
970
+ },
971
+ );
972
+
911
973
  # Ricoh composite tags
912
974
  %Image::ExifTool::Ricoh::Composite = (
913
975
  GROUPS => { 2 => 'Camera' },
@@ -932,6 +994,52 @@ my %ricohLensIDs = (
932
994
  Image::ExifTool::AddCompositeTags('Image::ExifTool::Ricoh');
933
995
 
934
996
 
997
+ #------------------------------------------------------------------------------
998
+ # Process Ricoh RDT* real-time metadata
999
+ # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
1000
+ # Returns: 1 on success, otherwise returns 0 and sets a Warning
1001
+ sub ProcessRicohRDT($$$)
1002
+ {
1003
+ my ($et, $dirInfo, $tagTablePtr) = @_;
1004
+ my $dataPt = $$dirInfo{DataPt};
1005
+ my $dirLen = $$dirInfo{DirLen};
1006
+ my $dirName = $$dirInfo{DirName};
1007
+ my ($i, $rdtg);
1008
+ return 0 if $dirLen < 16;
1009
+ my $ee = $et->Options('ExtractEmbedded');
1010
+ unless ($ee) {
1011
+ $et->WarnOnce('Use ExtractEmbedded option to read Ricoh real-time metadata',3);
1012
+ return 1;
1013
+ }
1014
+ my $endian = substr($$dataPt, 8, 2);
1015
+ SetByteOrder($endian eq "\x23\x01" ? 'II' : 'MM');
1016
+ my $count = Get32u($dataPt, 0);
1017
+ my $len = Get16u($dataPt, 6);
1018
+ if ($dirName eq 'RicohRDTG') {
1019
+ if ($ee < 2) {
1020
+ $et->WarnOnce('Set ExtractEmbedded option to 2 or higher to extract frame timestamps',3);
1021
+ return 1;
1022
+ }
1023
+ $rdtg = 0;
1024
+ $et->WarnOnce('Unexpected RDTG record length') if $len > 8;
1025
+ }
1026
+ if ($count * $len + 16 > $dirLen) {
1027
+ $et->Warn("Truncated $dirName data");
1028
+ $count = int(($dirLen - 16) / $len);
1029
+ }
1030
+ $et->VerboseDir($dirName);
1031
+ $$dirInfo{DirStart} = 16;
1032
+ $$dirInfo{DirLen} = $len;
1033
+ for ($i=0; $i<$count; ++$i) {;
1034
+ $$et{DOC_NUM} = ++$$et{DOC_COUNT};
1035
+ $et->HandleTag($tagTablePtr, 100, $rdtg++) if defined $rdtg;
1036
+ $et->ProcessBinaryData($dirInfo, $tagTablePtr);
1037
+ $$dirInfo{DirStart} += $len;
1038
+ }
1039
+ delete $$et{DOC_NUM};
1040
+ return 1;
1041
+ }
1042
+
935
1043
  #------------------------------------------------------------------------------
936
1044
  # Process Ricoh text-based maker notes
937
1045
  # Inputs: 0) ExifTool object reference
@@ -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.54';
25
+ $VERSION = '1.55';
26
26
 
27
27
  sub WriteSTMN($$$);
28
28
  sub ProcessINFO($$$);
@@ -904,9 +904,10 @@ my %formatMinMax = (
904
904
  Name => 'SamsungSvss',
905
905
  SubDirectory => { TagTable => 'Image::ExifTool::Samsung::svss' },
906
906
  },
907
+ mdln => 'SamsungModel', #PH (Samsung SM-A136U, etc)
907
908
  # swtr - 4 bytes, all zero
908
909
  # scid - 8 bytes, all zero
909
- # saut - 4 bytes, all zero
910
+ # saut - 4 or 6 bytes, all zero
910
911
  );
911
912
 
912
913
  # Samsung MP4 svss information (PH - from SM-C101 sample)
@@ -34,7 +34,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
34
34
  use Image::ExifTool::Exif;
35
35
  use Image::ExifTool::Minolta;
36
36
 
37
- $VERSION = '3.64';
37
+ $VERSION = '3.65';
38
38
 
39
39
  sub ProcessSRF($$$);
40
40
  sub ProcessSR2($$$);
@@ -1643,9 +1643,11 @@ my %hidUnk = ( Hidden => 1, Unknown => 1 );
1643
1643
  # 0x203e - int8u: seen 0, 1, 2, 3, 4 and 255: possibly nr. of detected/tracked faces?
1644
1644
  # 0x203f - int16u
1645
1645
  # 0x2041 - int8u
1646
- # 0x2044 - int32u[2] in ILCE-6700/7CM2/7CR, ILCE-7M4 v1.10, ILCE-7RM5 JPG and ZV-E1:
1647
- # ARW: seen 143360 53248 and 139264 53248
1648
- # JPG: [~filesize] 53248
1646
+ # 0x2044 - int32u[2] offset and size of some unknown data, relative to TIFF header in JPG and ARW - PH
1647
+ 0x2044 => {
1648
+ Name => 'HiddenInfo',
1649
+ SubDirectory => { TagTable => 'Image::ExifTool::Sony::HiddenInfo' },
1650
+ },
1649
1651
  # 0x2047 - first seen for ILCE-9M3, November 2023
1650
1652
  # 0x2048 - first seen for ILCE-9M3
1651
1653
  # 0x2049 - undef[2]
@@ -5958,6 +5960,29 @@ my %faceInfo = (
5958
5960
  # 0x001a, 0x001c appear to be 2 int16u values, meaning unknown
5959
5961
  );
5960
5962
 
5963
+ # hidden information (ref PH)
5964
+ %Image::ExifTool::Sony::HiddenInfo = (
5965
+ %binaryDataAttrs,
5966
+ GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
5967
+ FORMAT => 'int32u',
5968
+ IS_OFFSET => [ 0 ], # tag 0 is 'IsOffset'
5969
+ 0 => {
5970
+ Name => 'HiddenDataOffset',
5971
+ IsOffset => 1,
5972
+ OffsetPair => 1,
5973
+ DataTag => 'HiddenData',
5974
+ WriteGroup => 'MakerNotes',
5975
+ Protectd => 2,
5976
+ },
5977
+ 1 => {
5978
+ Name => 'HiddenDataLength',
5979
+ OffsetPair => 0,
5980
+ DataTag => 'HiddenData',
5981
+ WriteGroup => 'MakerNotes',
5982
+ Protectd => 2,
5983
+ },
5984
+ );
5985
+
5961
5986
  # shot information (ref PH)
5962
5987
  %Image::ExifTool::Sony::ShotInfo = (
5963
5988
  %binaryDataAttrs,
@@ -10735,6 +10760,32 @@ my %isoSetting2010 = (
10735
10760
  ValueConv => '$val[1] =~ /^W/i ? -$val[0] : $val[0]',
10736
10761
  PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
10737
10762
  },
10763
+ HiddenData => {
10764
+ Require => {
10765
+ # (Note: This pointer is fragile in JPEG images, and won't be updated
10766
+ # when the file is written unless the EXIF information is also written, but
10767
+ # an incorrect offset is fixed by subsequently writing EXIF with ExifTool)
10768
+ 0 => 'Sony:HiddenDataOffset',
10769
+ 1 => 'Sony:HiddenDataLength',
10770
+ },
10771
+ Notes => q{
10772
+ hidden data in some Sony JPG and ARW images, extracted only if specifically
10773
+ requested
10774
+ },
10775
+ RawConv => q{
10776
+ my $hdOff = $val[0];
10777
+ my $reqTag = $$self{REQ_TAG_LOOKUP}{hiddendata};
10778
+ my $hDump = $self->Options('HtmlDump');
10779
+ return undef unless $reqTag or $self->Options('Validate') or $hDump;
10780
+ my $dataPt = Image::ExifTool::Sony::ReadHiddenData($self, $hdOff, $val[1]);
10781
+ if ($dataPt and $hDump) {
10782
+ my $msg = '(Sony HiddenData)';
10783
+ $msg .= ' <span class=V>(fixed)</span>' if $hdOff != $val[0];
10784
+ $self->HDump($hdOff, $val[1], $msg, undef, 0x08);
10785
+ }
10786
+ return $reqTag ? $dataPt : undef;
10787
+ },
10788
+ },
10738
10789
  );
10739
10790
 
10740
10791
  # add our composite tags
@@ -10783,6 +10834,34 @@ sub SortLensTypes
10783
10834
  $$minoltaTypes{OTHER} = $other;
10784
10835
  }
10785
10836
 
10837
+ #------------------------------------------------------------------------------
10838
+ # Read HiddenData from JPEG trailer
10839
+ # Inputs: 0) ExifTool ref, 1) HiddenDataOffset (abs), 2) HiddenDataLength
10840
+ # Returns: HiddenData reference, or undef on error
10841
+ # --> updates $hdOff upon return if it was incorrect
10842
+ sub ReadHiddenData($$$)
10843
+ {
10844
+ my ($et, $hdOff, $hdLen) = @_;
10845
+ my $raf = $$et{RAF};
10846
+ my ($buff, $pos);
10847
+ unless ($raf->Seek($hdOff,0) and $raf->Read($buff,$hdLen) == $hdLen and
10848
+ $buff=~/^\x55\x26\x11\x05\0/)
10849
+ {
10850
+ # search the first 4096 bytes of the trailer to find the HiddenData
10851
+ unless ($$et{TrailerStart} and $raf->Seek($$et{TrailerStart},0) and
10852
+ $raf->Read($buff,4096) and $buff=~/\x55\x26\x11\x05\0/g and
10853
+ $pos = $$et{TrailerStart}+pos($buff)-5 and $raf->Seek($pos,0) and
10854
+ $raf->Read($buff,$hdLen) == $hdLen)
10855
+ {
10856
+ $et->Warn('Error reading HiddenData',1);
10857
+ return undef;
10858
+ }
10859
+ $_[1] = $pos; # return fixed offset
10860
+ $et->Warn('Fixed incorrect HiddenDataOffset',1) if $et->Options('Validate') or $$et{IsWriting};
10861
+ }
10862
+ return \$buff;
10863
+ }
10864
+
10786
10865
  #------------------------------------------------------------------------------
10787
10866
  # Process "SONY PIC\0" maker notes (DSC-H200/J10/W370/W510, MHS-TS20, ref PH)
10788
10867
  # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref