exiftool_vendored 13.12.0 → 13.14.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.
- 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
|
}
|