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.
- package/bin/Changes +87 -0
- package/bin/MANIFEST +1 -0
- package/bin/META.json +1 -1
- package/bin/META.yml +16 -16
- package/bin/README +3 -2
- package/bin/build_geolocation +7 -4
- package/bin/build_tag_lookup +29 -11
- package/bin/config_files/example.config +5 -0
- package/bin/config_files/onone.config +28 -0
- package/bin/exiftool +23 -15
- package/bin/lib/Image/ExifTool/AIFF.pm +8 -4
- package/bin/lib/Image/ExifTool/ASF.pm +4 -1
- package/bin/lib/Image/ExifTool/BuildTagLookup.pm +14 -8
- package/bin/lib/Image/ExifTool/Canon.pm +102 -12
- package/bin/lib/Image/ExifTool/CanonRaw.pm +1 -1
- package/bin/lib/Image/ExifTool/CanonVRD.pm +5 -2
- package/bin/lib/Image/ExifTool/FujiFilm.pm +46 -4
- package/bin/lib/Image/ExifTool/Geolocation.dat +0 -0
- package/bin/lib/Image/ExifTool/Geolocation.pm +6 -0
- package/bin/lib/Image/ExifTool/ID3.pm +39 -33
- package/bin/lib/Image/ExifTool/InDesign.pm +8 -4
- package/bin/lib/Image/ExifTool/Jpeg2000.pm +0 -1
- package/bin/lib/Image/ExifTool/Lang/de.pm +2 -2
- package/bin/lib/Image/ExifTool/Matroska.pm +66 -10
- package/bin/lib/Image/ExifTool/MinoltaRaw.pm +2 -2
- package/bin/lib/Image/ExifTool/Nikon.pm +36 -2
- package/bin/lib/Image/ExifTool/Olympus.pm +27 -17
- package/bin/lib/Image/ExifTool/PNG.pm +10 -2
- package/bin/lib/Image/ExifTool/Panasonic.pm +1 -0
- package/bin/lib/Image/ExifTool/PanasonicRaw.pm +1 -0
- package/bin/lib/Image/ExifTool/Pentax.pm +80 -14
- package/bin/lib/Image/ExifTool/QuickTime.pm +58 -13
- package/bin/lib/Image/ExifTool/QuickTimeStream.pl +111 -8
- package/bin/lib/Image/ExifTool/RIFF.pm +20 -10
- package/bin/lib/Image/ExifTool/Samsung.pm +56 -19
- package/bin/lib/Image/ExifTool/Sony.pm +21 -11
- package/bin/lib/Image/ExifTool/TagLookup.pm +6808 -6788
- package/bin/lib/Image/ExifTool/TagNames.pod +120 -55
- package/bin/lib/Image/ExifTool/WriteIPTC.pl +1 -1
- package/bin/lib/Image/ExifTool/WriteQuickTime.pl +84 -15
- package/bin/lib/Image/ExifTool/Writer.pl +7 -4
- package/bin/lib/Image/ExifTool/XMP.pm +8 -8
- package/bin/lib/Image/ExifTool/XMP2.pl +51 -30
- package/bin/lib/Image/ExifTool/ZIP.pm +8 -4
- package/bin/lib/Image/ExifTool.pm +65 -42
- package/bin/lib/Image/ExifTool.pod +28 -20
- package/bin/perl-Image-ExifTool.spec +1 -1
- package/bin/pp_build_exe.args +4 -4
- 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.
|
|
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 =>
|
|
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::
|
|
2340
|
-
ProcessProc => \&Image::ExifTool::
|
|
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
|
-
$
|
|
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
|
-
|
|
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 $
|
|
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
|
-
$
|
|
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
|
|
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
|
-
|
|
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
|
-
#
|
|
1402
|
-
# (some videos don't reference all freeGPS info from 'gps ' table, eg. INNOV
|
|
1403
|
-
|
|
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
|
-
}
|
|
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.
|
|
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
|
|
2045
|
-
$et->
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
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
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
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.
|
|
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
|
|
953
|
-
features (such as "Sound & Shot" or "Shot & More") from
|
|
954
|
-
as the Galaxy S4 and Tab S, and from the 'sefd' atom in
|
|
955
|
-
|
|
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{
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
1649
|
-
|
|
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;
|