exiftool_vendored 13.12.0 → 13.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/Changes +52 -22
- data/bin/MANIFEST +5 -0
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +2 -2
- data/bin/arg_files/exif2xmp.args +4 -0
- data/bin/arg_files/xmp2exif.args +2 -1
- data/bin/build_geolocation +1 -1
- data/bin/exiftool +5 -5
- data/bin/lib/Image/ExifTool/AFCP.pm +5 -5
- data/bin/lib/Image/ExifTool/BuildTagLookup.pm +24 -13
- data/bin/lib/Image/ExifTool/DJI.pm +64 -11
- data/bin/lib/Image/ExifTool/EXE.pm +2 -2
- data/bin/lib/Image/ExifTool/Geolocation.pm +16 -7
- data/bin/lib/Image/ExifTool/JPEG.pm +5 -1
- data/bin/lib/Image/ExifTool/LigoGPS.pm +1 -0
- data/bin/lib/Image/ExifTool/Nikon.pm +2 -0
- data/bin/lib/Image/ExifTool/Protobuf.pm +25 -7
- data/bin/lib/Image/ExifTool/QuickTime.pm +161 -50
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +2 -2
- data/bin/lib/Image/ExifTool/Samsung.pm +1 -1
- data/bin/lib/Image/ExifTool/TagLookup.pm +3438 -3430
- data/bin/lib/Image/ExifTool/TagNames.pod +89 -22
- data/bin/lib/Image/ExifTool/Torrent.pm +2 -2
- data/bin/lib/Image/ExifTool/Vivo.pm +124 -0
- data/bin/lib/Image/ExifTool/WriteQuickTime.pl +69 -40
- data/bin/lib/Image/ExifTool/Writer.pl +12 -8
- data/bin/lib/Image/ExifTool.pm +22 -6
- data/bin/lib/Image/ExifTool.pod +43 -41
- data/bin/perl-Image-ExifTool.spec +1 -1
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +3 -2
@@ -71,7 +71,7 @@ package Image::ExifTool::Geolocation;
|
|
71
71
|
use strict;
|
72
72
|
use vars qw($VERSION $geoDir $altDir $dbInfo);
|
73
73
|
|
74
|
-
$VERSION = '1.
|
74
|
+
$VERSION = '1.09'; # (this is the module version number, not the database version)
|
75
75
|
|
76
76
|
my $debug; # set to output processing time for testing
|
77
77
|
|
@@ -463,7 +463,7 @@ sub GetAltNames($;$)
|
|
463
463
|
sub Geolocate($;$)
|
464
464
|
{
|
465
465
|
my ($arg, $opts) = @_;
|
466
|
-
my ($city, @exact, %regex, @multiCity, $other, $idx, @cargs
|
466
|
+
my ($city, @exact, %regex, @multiCity, $other, $idx, @cargs);
|
467
467
|
my ($minPop, $minDistU, $minDistC, @matchParms, @coords, %fcOK, $both);
|
468
468
|
my ($pop, $maxDist, $multi, $fcodes, $altNames, @startTime);
|
469
469
|
|
@@ -594,8 +594,16 @@ Entry: for (; $i<@cityList; ++$i) {
|
|
594
594
|
}
|
595
595
|
@startTime and printf("= Processing time: %.3f sec\n", Time::HiRes::tv_interval(\@startTime));
|
596
596
|
if (%lastFound) {
|
597
|
-
@coords == 2
|
597
|
+
last if @coords == 2; # continue to use coords with last city matches
|
598
598
|
scalar(keys %lastFound) > 200 and warn("Too many matching cities\n"), return();
|
599
|
+
# return nearby cities if "num=" is used and only one match found
|
600
|
+
if ($num > 1 and scalar(keys %lastFound) == 1) {
|
601
|
+
my ($i) = keys %lastFound;
|
602
|
+
my @entry = GetEntry($i);
|
603
|
+
@coords = @entry[8,9];
|
604
|
+
SortDatabase('Latitude'); # (make sure we are sorted by latitude)
|
605
|
+
last;
|
606
|
+
}
|
599
607
|
unless (@lastByPop) {
|
600
608
|
@lastByPop = sort { $lastFound{$b} cmp $lastFound{$a} or $cityList[$a] cmp $cityList[$b] } keys %lastFound;
|
601
609
|
}
|
@@ -777,7 +785,7 @@ on the first call.
|
|
777
785
|
|
778
786
|
Sort database in specified order.
|
779
787
|
|
780
|
-
Image::ExifTool::Geolocation::
|
788
|
+
Image::ExifTool::Geolocation::SortDatabase('City');
|
781
789
|
|
782
790
|
=over 4
|
783
791
|
|
@@ -924,9 +932,10 @@ to the argument list:
|
|
924
932
|
both to determine the closest city matching the specified
|
925
933
|
name(s) instead of using GPS only.
|
926
934
|
|
927
|
-
'num=##' - When the search includes GPS coordinates,
|
928
|
-
|
929
|
-
|
935
|
+
'num=##' - When the search includes GPS coordinates, or when a single
|
936
|
+
city is matched by name, return the nearest ## cities instead
|
937
|
+
of just the closest or named one. Returned cities are in the
|
938
|
+
order from nearest to farthest.
|
930
939
|
|
931
940
|
See L<https://exiftool.org/geolocation.html#Read> for more details.
|
932
941
|
|
@@ -11,7 +11,7 @@ use strict;
|
|
11
11
|
use vars qw($VERSION);
|
12
12
|
use Image::ExifTool qw(:DataAccess :Utils);
|
13
13
|
|
14
|
-
$VERSION = '1.
|
14
|
+
$VERSION = '1.39';
|
15
15
|
|
16
16
|
sub ProcessOcad($$$);
|
17
17
|
sub ProcessJPEG_HDR($$$);
|
@@ -347,6 +347,10 @@ sub ProcessJPEG_HDR($$$);
|
|
347
347
|
Name => 'Samsung',
|
348
348
|
Condition => '$$valPt =~ /QDIOBS$/',
|
349
349
|
SubDirectory => { TagTable => 'Image::ExifTool::Samsung::Trailer' },
|
350
|
+
}, {
|
351
|
+
Name => 'Vivo',
|
352
|
+
Condition => '$$valPt =~ /^(streamdata|vivo\{")/',
|
353
|
+
SubDirectory => { TagTable => 'Image::ExifTool::Vivo::Main' },
|
350
354
|
}, {
|
351
355
|
Name => 'EmbeddedVideo',
|
352
356
|
Notes => 'extracted only when ExtractEmbedded option is used',
|
@@ -269,6 +269,7 @@ sub ParseLigoGPS($$$;$)
|
|
269
269
|
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
|
270
270
|
# 3) 1=LIGOGPS lat/lon/spd weren't fuzzed
|
271
271
|
# Returns: 1 on success
|
272
|
+
# Notes: The directory data should start with the string "LIGOGPSINFO\0"
|
272
273
|
sub ProcessLigoGPS($$$;$)
|
273
274
|
{
|
274
275
|
my ($et, $dirInfo, $tagTbl, $noFuzz) = @_;
|
@@ -3118,6 +3118,7 @@ my %base64coord = (
|
|
3118
3118
|
Condition => '$$valPt =~ /^040[012]/',
|
3119
3119
|
SubDirectory => { TagTable => 'Image::ExifTool::Nikon::AFInfo2V0400' },
|
3120
3120
|
},{ #JD
|
3121
|
+
# (expeed 5 processor cameras are version 0101, expeed 6 are version 03xx)
|
3121
3122
|
Name => 'AFInfo2',
|
3122
3123
|
# (this structure may be byte swapped when rewritten by CaptureNX)
|
3123
3124
|
SubDirectory => { TagTable => 'Image::ExifTool::Nikon::AFInfo2' },
|
@@ -4160,6 +4161,7 @@ my %base64coord = (
|
|
4160
4161
|
DATAMEMBER => [ 0, 4, 6 ],
|
4161
4162
|
NOTES => "These tags are written by Nikon DSLR's which have the live view feature.",
|
4162
4163
|
0 => {
|
4164
|
+
# (expeed 5 processor cameras are version 0101, expeed 6 are version 03xx)
|
4163
4165
|
Name => 'AFInfo2Version',
|
4164
4166
|
Format => 'undef[4]',
|
4165
4167
|
Writable => 0,
|
@@ -5,8 +5,9 @@
|
|
5
5
|
#
|
6
6
|
# Revisions: 2024-12-04 - P. Harvey Created
|
7
7
|
#
|
8
|
-
# Notes: Tag definitions for Protobuf tags support additional 'signed'
|
9
|
-
# and '
|
8
|
+
# Notes: Tag definitions for Protobuf tags support additional 'signed',
|
9
|
+
# 'unsigned' and 'int64s' formats for varInt (type 0) values,
|
10
|
+
# and 'rational' for byte (type 2) values
|
10
11
|
#
|
11
12
|
# References: 1) https://protobuf.dev/programming-guides/encoding/
|
12
13
|
#------------------------------------------------------------------------------
|
@@ -17,13 +18,13 @@ use strict;
|
|
17
18
|
use vars qw($VERSION);
|
18
19
|
use Image::ExifTool qw(:DataAccess :Utils);
|
19
20
|
|
20
|
-
$VERSION = '1.
|
21
|
+
$VERSION = '1.02';
|
21
22
|
|
22
23
|
sub ProcessProtobuf($$$;$);
|
23
24
|
|
24
25
|
#------------------------------------------------------------------------------
|
25
26
|
# Read bytes from dirInfo object
|
26
|
-
# Inputs: 0) dirInfo ref, 1) number of bytes
|
27
|
+
# Inputs: 0) dirInfo ref (with DataPt and Pos set), 1) number of bytes
|
27
28
|
# Returns: binary data or undef on error
|
28
29
|
sub GetBytes($$)
|
29
30
|
{
|
@@ -162,14 +163,31 @@ sub ProcessProtobuf($$$;$)
|
|
162
163
|
if ($$tagInfo{Format}) {
|
163
164
|
if ($type == 0) {
|
164
165
|
$val = $buff;
|
165
|
-
|
166
|
+
if ($$tagInfo{Format} eq 'signed') {
|
167
|
+
$val = ($val & 1) ? -($val >> 1)-1 : ($val >> 1);
|
168
|
+
} elsif ($$tagInfo{Format} eq 'int64s' and $val > 0xffffffff) {
|
169
|
+
# hack for DJI drones which store 64-bit signed integers improperly
|
170
|
+
# (just toss upper 32 bits which should be all 1's anyway)
|
171
|
+
$val = ($val & 0xffffffff) - 4294967296;
|
172
|
+
}
|
173
|
+
} elsif ($type == 2 and $$tagInfo{Format} eq 'rational') {
|
174
|
+
my $dir = { DataPt => \$buff, Pos => 0 };
|
175
|
+
my $num = VarInt($dir);
|
176
|
+
my $den = VarInt($dir);
|
177
|
+
$val = (defined $num and $den) ? "$num/$den" : 'err';
|
166
178
|
} else {
|
167
179
|
$val = ReadValue(\$buff, 0, $$tagInfo{Format}, undef, length($buff));
|
168
180
|
}
|
169
181
|
} elsif ($type == 0) {
|
170
182
|
$val = $buff;
|
171
|
-
my $
|
172
|
-
$
|
183
|
+
my $hex = sprintf('%x', $val);
|
184
|
+
if (length($hex) == 16 and $hex =~ /^ffffffff/) {
|
185
|
+
my $s64 = hex(substr($hex, 8)) - 4294967296;
|
186
|
+
$val .= " (0x$hex, int64s $s64)";
|
187
|
+
} else {
|
188
|
+
my $signed = ($val & 1) ? -($val >> 1)-1 : ($val >> 1);
|
189
|
+
$val .= " (0x$hex, signed $signed)";
|
190
|
+
}
|
173
191
|
} elsif ($type == 1) {
|
174
192
|
$val = '0x' . unpack('H*', $buff) . ' (double ' . GetDouble(\$buff,0) . ')';
|
175
193
|
} elsif ($type == 2) {
|
@@ -43,12 +43,12 @@
|
|
43
43
|
package Image::ExifTool::QuickTime;
|
44
44
|
|
45
45
|
use strict;
|
46
|
-
use vars qw($VERSION $AUTOLOAD %stringEncoding);
|
46
|
+
use vars qw($VERSION $AUTOLOAD %stringEncoding %avType);
|
47
47
|
use Image::ExifTool qw(:DataAccess :Utils);
|
48
48
|
use Image::ExifTool::Exif;
|
49
49
|
use Image::ExifTool::GPS;
|
50
50
|
|
51
|
-
$VERSION = '3.
|
51
|
+
$VERSION = '3.09';
|
52
52
|
|
53
53
|
sub ProcessMOV($$;$);
|
54
54
|
sub ProcessKeys($$$);
|
@@ -358,6 +358,19 @@ my %vendorID = (
|
|
358
358
|
5 => 'UTF16',
|
359
359
|
);
|
360
360
|
|
361
|
+
# media types for which we have separate Keys tables (AudioKeys, VideoKeys)
|
362
|
+
%avType = (
|
363
|
+
soun => 'Audio',
|
364
|
+
vide => 'Video',
|
365
|
+
);
|
366
|
+
|
367
|
+
# path to Keys/ItemList/UserData tags stored in tracks
|
368
|
+
my %trackPath = (
|
369
|
+
'MOV-Movie-Track-Meta-ItemList' => 'Keys',
|
370
|
+
'MOV-Movie-Track-UserData-Meta-ItemList' => 'ItemList',
|
371
|
+
'MOV-Movie-Track-UserData' => 'UserData',
|
372
|
+
);
|
373
|
+
|
361
374
|
my %graphicsMode = (
|
362
375
|
# (ref http://homepage.mac.com/vanhoek/MovieGuts%20docs/64.html)
|
363
376
|
0x00 => 'srcCopy',
|
@@ -594,13 +607,24 @@ my %userDefined = (
|
|
594
607
|
},
|
595
608
|
{
|
596
609
|
Name => 'LigoGPSInfo',
|
597
|
-
Condition => '$$valPt =~ /^LIGOGPSINFO\0/',
|
598
|
-
SubDirectory => {
|
610
|
+
Condition => '$$valPt =~ /^LIGOGPSINFO\0/ and $$self{OPTIONS}{ExtractEmbedded}',
|
611
|
+
SubDirectory => {
|
599
612
|
TagTable => 'Image::ExifTool::QuickTime::Stream',
|
600
613
|
ProcessProc => 'Image::ExifTool::LigoGPS::ProcessLigoGPS',
|
601
614
|
},
|
602
615
|
},
|
603
|
-
{
|
616
|
+
{
|
617
|
+
Name => 'Skip',
|
618
|
+
RawConv => q{
|
619
|
+
if ($val =~ /^LIGOGPSINFO\0/) {
|
620
|
+
$self->Warn('Use the ExtractEmbedded option to decode timed GPS',3);
|
621
|
+
return undef;
|
622
|
+
}
|
623
|
+
return $val;
|
624
|
+
},
|
625
|
+
Unknown => 1,
|
626
|
+
Binary => 1,
|
627
|
+
},
|
604
628
|
],
|
605
629
|
wide => { Unknown => 1, Binary => 1 },
|
606
630
|
ftyp => { #MP4
|
@@ -1525,7 +1549,7 @@ my %userDefined = (
|
|
1525
1549
|
# (this tag is readable/writable as a block through the Extra SphericalVideoXML tags)
|
1526
1550
|
Condition => '$$valPt=~/^\xff\xcc\x82\x63\xf8\x55\x4a\x93\x88\x14\x58\x7a\x02\x52\x1f\xdd/',
|
1527
1551
|
WriteGroup => 'GSpherical', # write only GSpherical XMP tags here
|
1528
|
-
|
1552
|
+
MediaType => 'vide', # only write in video tracks
|
1529
1553
|
SubDirectory => {
|
1530
1554
|
TagTable => 'Image::ExifTool::XMP::Main',
|
1531
1555
|
Start => 16,
|
@@ -2853,10 +2877,18 @@ my %userDefined = (
|
|
2853
2877
|
IgnoreProp => { NonRealTimeMeta => 1 }, # ignore container for Sony 'nrtm'
|
2854
2878
|
},
|
2855
2879
|
},
|
2856
|
-
'keys' => {
|
2880
|
+
'keys' => [{
|
2881
|
+
Name => 'AudioKeys',
|
2882
|
+
Condition => '$$self{MediaType} eq "soun"',
|
2883
|
+
SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AudioKeys' },
|
2884
|
+
},{
|
2885
|
+
Name => 'VideoKeys',
|
2886
|
+
Condition => '$$self{MediaType} eq "vide"',
|
2887
|
+
SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::VideoKeys' },
|
2888
|
+
},{
|
2857
2889
|
Name => 'Keys',
|
2858
2890
|
SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Keys' },
|
2859
|
-
},
|
2891
|
+
}],
|
2860
2892
|
bxml => {
|
2861
2893
|
Name => 'BinaryXML',
|
2862
2894
|
Flags => ['Binary','Unknown'],
|
@@ -3597,7 +3629,7 @@ my %userDefined = (
|
|
3597
3629
|
my $id = Image::ExifTool::ID3::GetGenreID($val);
|
3598
3630
|
return unless defined $id and $id =~ /^\d+$/;
|
3599
3631
|
return $id + 1;
|
3600
|
-
},
|
3632
|
+
},
|
3601
3633
|
},
|
3602
3634
|
egid => 'EpisodeGlobalUniqueID', #7
|
3603
3635
|
geID => { #10
|
@@ -6599,7 +6631,7 @@ my %userDefined = (
|
|
6599
6631
|
PROCESS_PROC => \&ProcessKeys,
|
6600
6632
|
WRITE_PROC => \&WriteKeys,
|
6601
6633
|
CHECK_PROC => \&CheckQTValue,
|
6602
|
-
VARS => { LONG_TAGS =>
|
6634
|
+
VARS => { LONG_TAGS => 8 },
|
6603
6635
|
WRITABLE => 1,
|
6604
6636
|
# (not PREFERRED when writing)
|
6605
6637
|
GROUPS => { 1 => 'Keys' },
|
@@ -6645,21 +6677,11 @@ my %userDefined = (
|
|
6645
6677
|
publisher => { },
|
6646
6678
|
software => { },
|
6647
6679
|
year => { Groups => { 2 => 'Time' } },
|
6648
|
-
'camera.identifier' => 'CameraIdentifier', # (iPhone 4)
|
6649
|
-
'camera.framereadouttimeinmicroseconds' => { # (iPhone 4)
|
6650
|
-
Name => 'FrameReadoutTime',
|
6651
|
-
ValueConv => '$val * 1e-6',
|
6652
|
-
ValueConvInv => 'int($val * 1e6 + 0.5)',
|
6653
|
-
PrintConv => '$val * 1e6 . " microseconds"',
|
6654
|
-
PrintConvInv => '$val =~ s/ .*//; $val * 1e-6',
|
6655
|
-
},
|
6656
|
-
# 'camera.focal_length.35mm_equivalent' - not top level (written to Keys in video track)
|
6657
|
-
# 'camera.lens_model' - not top level (written to Keys in video track)
|
6658
6680
|
'location.ISO6709' => {
|
6659
6681
|
Name => 'GPSCoordinates',
|
6660
6682
|
Groups => { 2 => 'Location' },
|
6661
6683
|
Notes => q{
|
6662
|
-
Google Photos may ignore this if the
|
6684
|
+
Google Photos may ignore this if the coordinates have more than 5 digits
|
6663
6685
|
after the decimal
|
6664
6686
|
},
|
6665
6687
|
ValueConv => \&ConvertISO6709,
|
@@ -6715,7 +6737,6 @@ my %userDefined = (
|
|
6715
6737
|
#
|
6716
6738
|
# the following tags aren't in the com.apple.quicktime namespace:
|
6717
6739
|
#
|
6718
|
-
'com.apple.photos.captureMode' => 'CaptureMode',
|
6719
6740
|
'com.android.version' => 'AndroidVersion',
|
6720
6741
|
'com.android.capture.fps' => { Name => 'AndroidCaptureFPS', Writable => 'float' },
|
6721
6742
|
'com.android.manufacturer' => 'AndroidMake',
|
@@ -6815,6 +6836,58 @@ my %userDefined = (
|
|
6815
6836
|
# (mdta)com.apple.proapps.image.{TIFF}.Software (eg. "9.0")
|
6816
6837
|
);
|
6817
6838
|
|
6839
|
+
# Keys tags in the audio track (ref PH)
|
6840
|
+
%Image::ExifTool::QuickTime::AudioKeys = (
|
6841
|
+
PROCESS_PROC => \&ProcessKeys,
|
6842
|
+
WRITE_PROC => \&WriteKeys,
|
6843
|
+
CHECK_PROC => \&CheckQTValue,
|
6844
|
+
WRITABLE => 1,
|
6845
|
+
GROUPS => { 1 => 'AudioKeys', 2 => 'Audio' },
|
6846
|
+
WRITE_GROUP => 'AudioKeys',
|
6847
|
+
LANG_INFO => \&GetLangInfo,
|
6848
|
+
NOTES => q{
|
6849
|
+
Keys tags written in the audio track by some Apple devices. These tags
|
6850
|
+
belong to the ExifTool AudioKeys family 1 gorup.
|
6851
|
+
},
|
6852
|
+
'player.movie.audio.gain' => 'AudioGain',
|
6853
|
+
'player.movie.audio.treble' => 'Treble',
|
6854
|
+
'player.movie.audio.bass' => 'Bass',
|
6855
|
+
'player.movie.audio.balance' => 'Balance',
|
6856
|
+
'player.movie.audio.pitchshift' => 'PitchShift',
|
6857
|
+
'player.movie.audio.mute' => {
|
6858
|
+
Name => 'Mute',
|
6859
|
+
Format => 'int8u',
|
6860
|
+
PrintConv => { 0 => 'Off', 1 => 'On' },
|
6861
|
+
},
|
6862
|
+
);
|
6863
|
+
|
6864
|
+
# Keys tags in the video track (ref PH)
|
6865
|
+
%Image::ExifTool::QuickTime::VideoKeys = (
|
6866
|
+
PROCESS_PROC => \&ProcessKeys,
|
6867
|
+
WRITE_PROC => \&WriteKeys,
|
6868
|
+
CHECK_PROC => \&CheckQTValue,
|
6869
|
+
VARS => { LONG_TAGS => 2 },
|
6870
|
+
WRITABLE => 1,
|
6871
|
+
GROUPS => { 1 => 'VideoKeys', 2 => 'Camera' },
|
6872
|
+
WRITE_GROUP => 'VideoKeys',
|
6873
|
+
LANG_INFO => \&GetLangInfo,
|
6874
|
+
NOTES => q{
|
6875
|
+
Keys tags written in the video track. These tags belong to the ExifTool
|
6876
|
+
VideoKeys family 1 gorup.
|
6877
|
+
},
|
6878
|
+
'camera.identifier' => 'CameraIdentifier',
|
6879
|
+
'camera.lens_model' => 'LensModel',
|
6880
|
+
'camera.focal_length.35mm_equivalent' => 'FocalLengthIn35mmFormat',
|
6881
|
+
'camera.framereadouttimeinmicroseconds' => {
|
6882
|
+
Name => 'FrameReadoutTime',
|
6883
|
+
ValueConv => '$val * 1e-6',
|
6884
|
+
ValueConvInv => 'int($val * 1e6 + 0.5)',
|
6885
|
+
PrintConv => '$val * 1e6 . " microseconds"',
|
6886
|
+
PrintConvInv => '$val =~ s/ .*//; $val * 1e-6',
|
6887
|
+
},
|
6888
|
+
'com.apple.photos.captureMode' => 'CaptureMode',
|
6889
|
+
);
|
6890
|
+
|
6818
6891
|
# iTunes info ('----') atoms
|
6819
6892
|
%Image::ExifTool::QuickTime::iTunesInfo = (
|
6820
6893
|
PROCESS_PROC => \&ProcessMOV,
|
@@ -7280,7 +7353,7 @@ my %userDefined = (
|
|
7280
7353
|
{
|
7281
7354
|
Name => 'VideoFrameRate',
|
7282
7355
|
Notes => 'average rate calculated from time-to-sample table for video media',
|
7283
|
-
Condition => '$$self{
|
7356
|
+
Condition => '$$self{MediaType} eq "vide"',
|
7284
7357
|
Format => 'undef', # (necessary to prevent decoding as string!)
|
7285
7358
|
# (must be RawConv so appropriate MediaTS is used in calculation)
|
7286
7359
|
RawConv => 'Image::ExifTool::QuickTime::CalcSampleRate($self, \$val)',
|
@@ -7871,7 +7944,6 @@ my %userDefined = (
|
|
7871
7944
|
mode => 'ModeFlags', #PH (?) 0x04 is HD flag (https://compilr.com/heksesang/requiem-mac/UnDrm.java)
|
7872
7945
|
# sing - seen 4 zeros
|
7873
7946
|
# hi32 - seen "00 00 00 04"
|
7874
|
-
|
7875
7947
|
);
|
7876
7948
|
|
7877
7949
|
# MP4 hint sample description box (ref 5)
|
@@ -8119,6 +8191,7 @@ my %userDefined = (
|
|
8119
8191
|
Format => 'undef[4]',
|
8120
8192
|
RawConv => q{
|
8121
8193
|
$$self{HandlerType} = $val unless $val eq 'alis' or $val eq 'url ';
|
8194
|
+
$$self{MediaType} = $val if @{$$self{PATH}} > 1 and $$self{PATH}[-2] eq 'Media';
|
8122
8195
|
$$self{HasHandler}{$val} = 1; # remember all our handlers
|
8123
8196
|
return $val;
|
8124
8197
|
},
|
@@ -9538,6 +9611,8 @@ sub ProcessKeys($$$)
|
|
9538
9611
|
my $groups = $$tagInfo{Groups};
|
9539
9612
|
$$newInfo{Groups} = $groups ? { %$groups } : { };
|
9540
9613
|
$$newInfo{Groups}{$_} or $$newInfo{Groups}{$_} = $$tagTablePtr{GROUPS}{$_} foreach 0..2;
|
9614
|
+
# set Keys group. This is necessary for logic when reading the associated ItemList entry,
|
9615
|
+
# but note that the group name will be overridden by TAG_EXTRA G1 for tags in a track
|
9541
9616
|
$$newInfo{Groups}{1} = 'Keys';
|
9542
9617
|
} elsif ($tag =~ /^[-\w. ]+$/ or $tag =~ /\w{4}/) {
|
9543
9618
|
# create info for tags with reasonable id's
|
@@ -9605,7 +9680,7 @@ sub ProcessMOV($$;$)
|
|
9605
9680
|
|
9606
9681
|
my $topLevel = not $$et{InQuickTime};
|
9607
9682
|
$$et{InQuickTime} = 1;
|
9608
|
-
$$et{HandlerType} = $$et{MetaFormat} = ''
|
9683
|
+
$$et{HandlerType} = $$et{MetaFormat} = $$et{MediaType} = '' if $topLevel;
|
9609
9684
|
|
9610
9685
|
unless (defined $$et{KeysCount}) {
|
9611
9686
|
$$et{KeysCount} = 0; # initialize ItemList key directory count
|
@@ -9631,13 +9706,15 @@ sub ProcessMOV($$;$)
|
|
9631
9706
|
}
|
9632
9707
|
($size, $tag) = unpack('Na4', $buff);
|
9633
9708
|
my $fast = $$et{OPTIONS}{FastScan} || 0;
|
9634
|
-
# check for Insta360 trailer
|
9709
|
+
# check for Insta360 or LIGOGPSINFO trailer
|
9635
9710
|
if ($topLevel and not $fast) {
|
9636
9711
|
my $pos = $raf->Tell();
|
9637
|
-
if ($raf->Seek(-40, 2) and $raf->Read($buff, 40) == 40
|
9638
|
-
substr($buff, 8) eq '8db42d694ccc418790edff439fe026bf')
|
9639
|
-
|
9640
|
-
|
9712
|
+
if ($raf->Seek(-40, 2) and $raf->Read($buff, 40) == 40) {
|
9713
|
+
if (substr($buff, 8) eq '8db42d694ccc418790edff439fe026bf') {
|
9714
|
+
$trailer = [ 'Insta360', $raf->Tell() - unpack('V',$buff) ];
|
9715
|
+
} elsif ($buff =~ /\&\&\&\&(.{4})$/) {
|
9716
|
+
$trailer = [ 'LigoGPS', $raf->Tell() - Get32u(\$buff, 36) ];
|
9717
|
+
}
|
9641
9718
|
}
|
9642
9719
|
$raf->Seek($pos,0) or return 0;
|
9643
9720
|
}
|
@@ -9754,16 +9831,18 @@ sub ProcessMOV($$;$)
|
|
9754
9831
|
}
|
9755
9832
|
if ($isUserData and $$et{SET_GROUP1}) {
|
9756
9833
|
my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
|
9757
|
-
|
9758
|
-
|
9759
|
-
|
9760
|
-
|
9761
|
-
|
9762
|
-
|
9763
|
-
|
9764
|
-
|
9834
|
+
unless ($$tagInfo{SubDirectory}) {
|
9835
|
+
# add track name to UserData tags inside tracks
|
9836
|
+
$tag = $$et{SET_GROUP1} . $tag;
|
9837
|
+
if (not $$tagTablePtr{$tag} and $tagInfo) {
|
9838
|
+
my %newInfo = %$tagInfo;
|
9839
|
+
foreach ('Name', 'Description') {
|
9840
|
+
next unless $$tagInfo{$_};
|
9841
|
+
$newInfo{$_} = $$et{SET_GROUP1} . $$tagInfo{$_};
|
9842
|
+
$newInfo{$_} =~ s/^(Track\d+)Track/$1/; # remove duplicate "Track" in name
|
9843
|
+
}
|
9844
|
+
AddTagToTable($tagTablePtr, $tag, \%newInfo);
|
9765
9845
|
}
|
9766
|
-
AddTagToTable($tagTablePtr, $tag, \%newInfo);
|
9767
9846
|
}
|
9768
9847
|
}
|
9769
9848
|
# set flag to store additional information for ExtractEmbedded option
|
@@ -9830,7 +9909,7 @@ sub ProcessMOV($$;$)
|
|
9830
9909
|
# check for RIFF trailer (written by Auto-Vox dashcam)
|
9831
9910
|
if ($buff =~ /^(gpsa|gps0|gsen|gsea)...\0/s) { # (yet seen only gpsa as first record)
|
9832
9911
|
$et->VPrint(0, sprintf("Found RIFF trailer at offset 0x%x",$lastPos));
|
9833
|
-
if ($
|
9912
|
+
if ($ee) {
|
9834
9913
|
$raf->Seek(-8, 1) or last; # seek back to start of trailer
|
9835
9914
|
my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
|
9836
9915
|
ProcessRIFFTrailer($et, { RAF => $raf }, $tbl);
|
@@ -9980,6 +10059,7 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
|
|
9980
10059
|
}
|
9981
10060
|
}
|
9982
10061
|
if ($tagInfo) {
|
10062
|
+
my @found;
|
9983
10063
|
my $subdir = $$tagInfo{SubDirectory};
|
9984
10064
|
if ($subdir) {
|
9985
10065
|
my $start = $$subdir{Start} || 0;
|
@@ -10098,21 +10178,23 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
|
|
10098
10178
|
Index => $index,
|
10099
10179
|
Extra => sprintf(", Type='${type}', Flags=0x%x%s, Lang=0x%.4x",$flags,$str,$lang),
|
10100
10180
|
) if $verbose;
|
10101
|
-
|
10102
|
-
|
10103
|
-
|
10104
|
-
|
10105
|
-
|
10181
|
+
if (defined $value) {
|
10182
|
+
# use "Keys" in path instead of ItemList if this was defined by a Keys tag
|
10183
|
+
# (the only reason for this is to have "Keys" in the family 5 group name)
|
10184
|
+
# Note that the Keys group is specifically set by the ProcessKeys routine,
|
10185
|
+
# even though this tag would be in the ItemList table
|
10186
|
+
my $isKeys = $$tagInfo{Groups} && $$tagInfo{Groups}{1} && $$tagInfo{Groups}{1} eq 'Keys';
|
10187
|
+
$isKeys and $oldDir = $$et{PATH}[-1], $$et{PATH}[-1] = 'Keys';
|
10188
|
+
push @found, $et->FoundTag($langInfo, $value);
|
10189
|
+
$$et{PATH}[-1] = $oldDir if $isKeys;
|
10106
10190
|
}
|
10107
|
-
$et->FoundTag($langInfo, $value) if defined $value;
|
10108
|
-
$$et{PATH}[-1] = $oldDir if $isKey;
|
10109
10191
|
$pos += $len;
|
10110
10192
|
}
|
10111
10193
|
} elsif ($tag =~ /^\xa9/ or $$tagInfo{IText}) {
|
10112
10194
|
# parse international text to extract all languages
|
10113
10195
|
my $pos = 0;
|
10114
10196
|
if ($$tagInfo{Format}) {
|
10115
|
-
$et->FoundTag($tagInfo, ReadValue(\$val, 0, $$tagInfo{Format}, undef, length($val)));
|
10197
|
+
push @found, $et->FoundTag($tagInfo, ReadValue(\$val, 0, $$tagInfo{Format}, undef, length($val)));
|
10116
10198
|
$pos = $size;
|
10117
10199
|
}
|
10118
10200
|
for (;;) {
|
@@ -10177,7 +10259,7 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
|
|
10177
10259
|
$str = substr($val, $pos-$n-2, $n) . $str;
|
10178
10260
|
}
|
10179
10261
|
$langInfo = GetLangInfoQT($et, $tagInfo, $lang) if $lang;
|
10180
|
-
$et->FoundTag($langInfo || $tagInfo, $str);
|
10262
|
+
push @found, $et->FoundTag($langInfo || $tagInfo, $str);
|
10181
10263
|
$pos += $len;
|
10182
10264
|
}
|
10183
10265
|
} else {
|
@@ -10191,6 +10273,7 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
|
|
10191
10273
|
$$et{BASE} = $dataPos;
|
10192
10274
|
}
|
10193
10275
|
my $key = $et->FoundTag($tagInfo, $val);
|
10276
|
+
push @found, $key;
|
10194
10277
|
$$et{BASE} = $oldBase if defined $oldBase;
|
10195
10278
|
# decode if necessary (NOTE: must be done after RawConv)
|
10196
10279
|
if (defined $key and (not $format or $format =~ /^string/) and
|
@@ -10206,6 +10289,14 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
|
|
10206
10289
|
}
|
10207
10290
|
}
|
10208
10291
|
}
|
10292
|
+
# tweak family 1 group names for Keys/ItemList/UserData tags in a track
|
10293
|
+
if ($$et{SET_GROUP1} and ($dirID eq 'ilst' or $dirID eq 'udta') and @found) {
|
10294
|
+
my $type = $trackPath{join '-', @{$$et{PATH}}};
|
10295
|
+
if ($type) {
|
10296
|
+
my $grp = ($avType{$$et{MediaType}} || $$et{SET_GROUP1}) . $type;
|
10297
|
+
defined and $et->SetGroup($_, $grp) foreach @found;
|
10298
|
+
}
|
10299
|
+
}
|
10209
10300
|
}
|
10210
10301
|
} else {
|
10211
10302
|
$et->VerboseInfo($tag, $tagInfo,
|
@@ -10218,6 +10309,7 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
|
|
10218
10309
|
last;
|
10219
10310
|
}
|
10220
10311
|
}
|
10312
|
+
$$et{MediaType} = '' if $tag eq 'trak'; # reset track type at end of track
|
10221
10313
|
$dataPos += $size + 8; # point to start of next atom data
|
10222
10314
|
last if $dirEnd and $dataPos >= $dirEnd; # (note: ignores last value if 0 bytes)
|
10223
10315
|
$lastPos = $raf->Tell() + $dirBase;
|
@@ -10273,7 +10365,26 @@ QTLang: foreach $tag (@{$$et{QTLang}}) {
|
|
10273
10365
|
# process item information now that we are done processing its 'meta' container
|
10274
10366
|
HandleItemInfo($et) if $topLevel or $dirID eq 'meta';
|
10275
10367
|
|
10276
|
-
|
10368
|
+
# process LigoGPS trailer now if it exists and we haven't already processed it
|
10369
|
+
if ($trailer and $$trailer[0] eq 'LigoGPS' and $lastPos <= $$trailer[1] and
|
10370
|
+
$raf->Seek($$trailer[1]) and $raf->Read($buff, 8) == 8 and $buff =~ /skip$/)
|
10371
|
+
{
|
10372
|
+
if ($ee) {
|
10373
|
+
my $len = Get32u(\$buff, 0) - 16;
|
10374
|
+
if ($len > 0 and $raf->Read($buff, $len) == $len and $buff =~ /^LIGOGPSINFO\0/) {
|
10375
|
+
my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
|
10376
|
+
my %dirInfo = ( DataPt => \$buff, DataPos => $$trailer[1] + 8, DirName => 'LigoGPSTrailer' );
|
10377
|
+
Image::ExifTool::LigoGPS::ProcessLigoGPS($et, \%dirInfo, $tbl);
|
10378
|
+
} else {
|
10379
|
+
$et->Warn('Unrecognized data in LigoGPS trailer');
|
10380
|
+
}
|
10381
|
+
} else {
|
10382
|
+
$et->Warn('Use the ExtractEmbedded option to decode timed GPS',3);
|
10383
|
+
}
|
10384
|
+
}
|
10385
|
+
# brute force scan for metadata embedded in media data
|
10386
|
+
# (and process Insta360 trailer if it exists)
|
10387
|
+
ScanMediaData($et) if $ee and $topLevel;
|
10277
10388
|
|
10278
10389
|
# restore any changed options
|
10279
10390
|
$et->Options($_ => $saveOptions{$_}) foreach keys %saveOptions;
|
@@ -111,7 +111,7 @@ my %insvLimit = (
|
|
111
111
|
The tags below are extracted from timed metadata in QuickTime and other
|
112
112
|
formats of video files when the ExtractEmbedded option is used. Although
|
113
113
|
most of these tags are combined into the single table below, ExifTool
|
114
|
-
currently reads
|
114
|
+
currently reads 100 different types of timed GPS metadata from video files.
|
115
115
|
},
|
116
116
|
VARS => { NO_ID => 1 },
|
117
117
|
GPSLatitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', RawConv => '$$self{FoundGPSLatitude} = 1; $val' },
|
@@ -1512,7 +1512,7 @@ Sample: for ($i=0; ; ) {
|
|
1512
1512
|
# clean up
|
1513
1513
|
$raf->Seek($tell, 0); # restore original file position
|
1514
1514
|
delete $$et{DOC_NUM};
|
1515
|
-
$$et{HandlerType} =
|
1515
|
+
$$et{HandlerType} = '';
|
1516
1516
|
}
|
1517
1517
|
|
1518
1518
|
#------------------------------------------------------------------------------
|
@@ -1718,7 +1718,7 @@ SamBlock:
|
|
1718
1718
|
$fixup->AddFixup(length($buff) - $offsetPos);
|
1719
1719
|
$fixup->AddFixup(length($buff) - $offsetPos + 4);
|
1720
1720
|
}
|
1721
|
-
$et->VPrint(0, "Writing Samsung trailer ($dirLen bytes)\n") if $verbose;
|
1721
|
+
$et->VPrint(0, " Writing Samsung trailer ($dirLen bytes)\n") if $verbose;
|
1722
1722
|
Write($$dirInfo{OutFile}, $buff) or return -1;
|
1723
1723
|
return 1;
|
1724
1724
|
}
|