exiftool_vendored 13.12.0 → 13.16.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 +79 -22
- data/bin/MANIFEST +7 -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 +6 -5
- data/bin/lib/Image/ExifTool/AFCP.pm +5 -5
- data/bin/lib/Image/ExifTool/BuildTagLookup.pm +20 -15
- data/bin/lib/Image/ExifTool/Canon.pm +5 -3
- data/bin/lib/Image/ExifTool/DJI.pm +64 -11
- data/bin/lib/Image/ExifTool/EXE.pm +17 -3
- data/bin/lib/Image/ExifTool/Geolocation.pm +16 -7
- data/bin/lib/Image/ExifTool/ID3.pm +4 -4
- data/bin/lib/Image/ExifTool/JPEG.pm +5 -1
- data/bin/lib/Image/ExifTool/LigoGPS.pm +1 -0
- data/bin/lib/Image/ExifTool/MIE.pm +6 -3
- data/bin/lib/Image/ExifTool/Nikon.pm +328 -4
- data/bin/lib/Image/ExifTool/NikonCustom.pm +1 -1
- data/bin/lib/Image/ExifTool/Panasonic.pm +7 -1
- data/bin/lib/Image/ExifTool/Protobuf.pm +25 -7
- data/bin/lib/Image/ExifTool/QuickTime.pm +215 -59
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +12 -12
- data/bin/lib/Image/ExifTool/README +4 -1
- data/bin/lib/Image/ExifTool/RIFF.pm +11 -1
- data/bin/lib/Image/ExifTool/Samsung.pm +1 -1
- data/bin/lib/Image/ExifTool/TagLookup.pm +4821 -4811
- data/bin/lib/Image/ExifTool/TagNames.pod +231 -25
- 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 +114 -63
- data/bin/lib/Image/ExifTool/WriteRIFF.pl +3 -1
- data/bin/lib/Image/ExifTool/Writer.pl +16 -11
- data/bin/lib/Image/ExifTool.pm +24 -8
- data/bin/lib/Image/ExifTool.pod +52 -49
- data/bin/perl-Image-ExifTool.spec +1 -1
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +3 -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.11';
|
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'],
|
@@ -3490,7 +3522,11 @@ my %userDefined = (
|
|
3490
3522
|
#
|
3491
3523
|
'----' => {
|
3492
3524
|
Name => 'iTunesInfo',
|
3493
|
-
|
3525
|
+
Deletable => 1, # (deletable via 'iTunes' group)
|
3526
|
+
SubDirectory => {
|
3527
|
+
TagTable => 'Image::ExifTool::QuickTime::iTunesInfo',
|
3528
|
+
DirName => 'iTunes', # (necessary for group 'iTunes' delete)
|
3529
|
+
},
|
3494
3530
|
},
|
3495
3531
|
aART => { Name => 'AlbumArtist', Groups => { 2 => 'Author' } },
|
3496
3532
|
covr => { Name => 'CoverArt', Groups => { 2 => 'Preview' }, Binary => 1 },
|
@@ -3597,7 +3633,7 @@ my %userDefined = (
|
|
3597
3633
|
my $id = Image::ExifTool::ID3::GetGenreID($val);
|
3598
3634
|
return unless defined $id and $id =~ /^\d+$/;
|
3599
3635
|
return $id + 1;
|
3600
|
-
},
|
3636
|
+
},
|
3601
3637
|
},
|
3602
3638
|
egid => 'EpisodeGlobalUniqueID', #7
|
3603
3639
|
geID => { #10
|
@@ -6599,7 +6635,7 @@ my %userDefined = (
|
|
6599
6635
|
PROCESS_PROC => \&ProcessKeys,
|
6600
6636
|
WRITE_PROC => \&WriteKeys,
|
6601
6637
|
CHECK_PROC => \&CheckQTValue,
|
6602
|
-
VARS => { LONG_TAGS =>
|
6638
|
+
VARS => { LONG_TAGS => 8 },
|
6603
6639
|
WRITABLE => 1,
|
6604
6640
|
# (not PREFERRED when writing)
|
6605
6641
|
GROUPS => { 1 => 'Keys' },
|
@@ -6645,21 +6681,11 @@ my %userDefined = (
|
|
6645
6681
|
publisher => { },
|
6646
6682
|
software => { },
|
6647
6683
|
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
6684
|
'location.ISO6709' => {
|
6659
6685
|
Name => 'GPSCoordinates',
|
6660
6686
|
Groups => { 2 => 'Location' },
|
6661
6687
|
Notes => q{
|
6662
|
-
Google Photos may ignore this if the
|
6688
|
+
Google Photos may ignore this if the coordinates have more than 5 digits
|
6663
6689
|
after the decimal
|
6664
6690
|
},
|
6665
6691
|
ValueConv => \&ConvertISO6709,
|
@@ -6715,7 +6741,6 @@ my %userDefined = (
|
|
6715
6741
|
#
|
6716
6742
|
# the following tags aren't in the com.apple.quicktime namespace:
|
6717
6743
|
#
|
6718
|
-
'com.apple.photos.captureMode' => 'CaptureMode',
|
6719
6744
|
'com.android.version' => 'AndroidVersion',
|
6720
6745
|
'com.android.capture.fps' => { Name => 'AndroidCaptureFPS', Writable => 'float' },
|
6721
6746
|
'com.android.manufacturer' => 'AndroidMake',
|
@@ -6815,6 +6840,58 @@ my %userDefined = (
|
|
6815
6840
|
# (mdta)com.apple.proapps.image.{TIFF}.Software (eg. "9.0")
|
6816
6841
|
);
|
6817
6842
|
|
6843
|
+
# Keys tags in the audio track (ref PH)
|
6844
|
+
%Image::ExifTool::QuickTime::AudioKeys = (
|
6845
|
+
PROCESS_PROC => \&ProcessKeys,
|
6846
|
+
WRITE_PROC => \&WriteKeys,
|
6847
|
+
CHECK_PROC => \&CheckQTValue,
|
6848
|
+
WRITABLE => 1,
|
6849
|
+
GROUPS => { 1 => 'AudioKeys', 2 => 'Audio' },
|
6850
|
+
WRITE_GROUP => 'AudioKeys',
|
6851
|
+
LANG_INFO => \&GetLangInfo,
|
6852
|
+
NOTES => q{
|
6853
|
+
Keys tags written in the audio track by some Apple devices. These tags
|
6854
|
+
belong to the ExifTool AudioKeys family 1 gorup.
|
6855
|
+
},
|
6856
|
+
'player.movie.audio.gain' => 'AudioGain',
|
6857
|
+
'player.movie.audio.treble' => 'Treble',
|
6858
|
+
'player.movie.audio.bass' => 'Bass',
|
6859
|
+
'player.movie.audio.balance' => 'Balance',
|
6860
|
+
'player.movie.audio.pitchshift' => 'PitchShift',
|
6861
|
+
'player.movie.audio.mute' => {
|
6862
|
+
Name => 'Mute',
|
6863
|
+
Format => 'int8u',
|
6864
|
+
PrintConv => { 0 => 'Off', 1 => 'On' },
|
6865
|
+
},
|
6866
|
+
);
|
6867
|
+
|
6868
|
+
# Keys tags in the video track (ref PH)
|
6869
|
+
%Image::ExifTool::QuickTime::VideoKeys = (
|
6870
|
+
PROCESS_PROC => \&ProcessKeys,
|
6871
|
+
WRITE_PROC => \&WriteKeys,
|
6872
|
+
CHECK_PROC => \&CheckQTValue,
|
6873
|
+
VARS => { LONG_TAGS => 2 },
|
6874
|
+
WRITABLE => 1,
|
6875
|
+
GROUPS => { 1 => 'VideoKeys', 2 => 'Camera' },
|
6876
|
+
WRITE_GROUP => 'VideoKeys',
|
6877
|
+
LANG_INFO => \&GetLangInfo,
|
6878
|
+
NOTES => q{
|
6879
|
+
Keys tags written in the video track. These tags belong to the ExifTool
|
6880
|
+
VideoKeys family 1 gorup.
|
6881
|
+
},
|
6882
|
+
'camera.identifier' => 'CameraIdentifier',
|
6883
|
+
'camera.lens_model' => 'LensModel',
|
6884
|
+
'camera.focal_length.35mm_equivalent' => 'FocalLengthIn35mmFormat',
|
6885
|
+
'camera.framereadouttimeinmicroseconds' => {
|
6886
|
+
Name => 'FrameReadoutTime',
|
6887
|
+
ValueConv => '$val * 1e-6',
|
6888
|
+
ValueConvInv => 'int($val * 1e6 + 0.5)',
|
6889
|
+
PrintConv => '$val * 1e6 . " microseconds"',
|
6890
|
+
PrintConvInv => '$val =~ s/ .*//; $val * 1e-6',
|
6891
|
+
},
|
6892
|
+
'com.apple.photos.captureMode' => 'CaptureMode',
|
6893
|
+
);
|
6894
|
+
|
6818
6895
|
# iTunes info ('----') atoms
|
6819
6896
|
%Image::ExifTool::QuickTime::iTunesInfo = (
|
6820
6897
|
PROCESS_PROC => \&ProcessMOV,
|
@@ -7280,7 +7357,7 @@ my %userDefined = (
|
|
7280
7357
|
{
|
7281
7358
|
Name => 'VideoFrameRate',
|
7282
7359
|
Notes => 'average rate calculated from time-to-sample table for video media',
|
7283
|
-
Condition => '$$self{
|
7360
|
+
Condition => '$$self{MediaType} eq "vide"',
|
7284
7361
|
Format => 'undef', # (necessary to prevent decoding as string!)
|
7285
7362
|
# (must be RawConv so appropriate MediaTS is used in calculation)
|
7286
7363
|
RawConv => 'Image::ExifTool::QuickTime::CalcSampleRate($self, \$val)',
|
@@ -7871,7 +7948,6 @@ my %userDefined = (
|
|
7871
7948
|
mode => 'ModeFlags', #PH (?) 0x04 is HD flag (https://compilr.com/heksesang/requiem-mac/UnDrm.java)
|
7872
7949
|
# sing - seen 4 zeros
|
7873
7950
|
# hi32 - seen "00 00 00 04"
|
7874
|
-
|
7875
7951
|
);
|
7876
7952
|
|
7877
7953
|
# MP4 hint sample description box (ref 5)
|
@@ -8119,6 +8195,7 @@ my %userDefined = (
|
|
8119
8195
|
Format => 'undef[4]',
|
8120
8196
|
RawConv => q{
|
8121
8197
|
$$self{HandlerType} = $val unless $val eq 'alis' or $val eq 'url ';
|
8198
|
+
$$self{MediaType} = $val if @{$$self{PATH}} > 1 and $$self{PATH}[-2] eq 'Media';
|
8122
8199
|
$$self{HasHandler}{$val} = 1; # remember all our handlers
|
8123
8200
|
return $val;
|
8124
8201
|
},
|
@@ -8703,24 +8780,28 @@ sub UnpackLang($;$)
|
|
8703
8780
|
# Get language code string given QuickTime language and country codes
|
8704
8781
|
# Inputs: 0) numerical language code, 1) numerical country code, 2) no defaults
|
8705
8782
|
# Returns: language code string (ie. "fra-FR") or undef for default language
|
8783
|
+
# ex) 0x15c7 0x0000 is 'eng' with no country (ie. returns 'und' unless $noDef)
|
8784
|
+
# 0x15c7 0x5553 is 'eng-US'
|
8785
|
+
# 0x1a41 0x4652 is 'fra-FR'
|
8786
|
+
# 0x55c4 is 'und'
|
8706
8787
|
sub GetLangCode($;$$)
|
8707
8788
|
{
|
8708
8789
|
my ($lang, $ctry, $noDef) = @_;
|
8709
8790
|
# ignore country ('ctry') and language lists ('lang') for now
|
8710
8791
|
undef $ctry if $ctry and $ctry <= 255;
|
8711
8792
|
undef $lang if $lang and $lang <= 255;
|
8712
|
-
$
|
8793
|
+
my $langCode = UnpackLang($lang, $noDef);
|
8713
8794
|
# add country code if specified
|
8714
8795
|
if ($ctry) {
|
8715
8796
|
$ctry = unpack('a2',pack('n',$ctry)); # unpack as ISO 3166-1
|
8716
8797
|
# treat 'ZZ' like a default country (see ref 12)
|
8717
8798
|
undef $ctry if $ctry eq 'ZZ';
|
8718
8799
|
if ($ctry and $ctry =~ /^[A-Z]{2}$/) {
|
8719
|
-
$
|
8720
|
-
$
|
8800
|
+
$langCode or $langCode = UnpackLang($lang,1) || 'und';
|
8801
|
+
$langCode .= "-$ctry";
|
8721
8802
|
}
|
8722
8803
|
}
|
8723
|
-
return $
|
8804
|
+
return $langCode;
|
8724
8805
|
}
|
8725
8806
|
|
8726
8807
|
#------------------------------------------------------------------------------
|
@@ -9538,6 +9619,8 @@ sub ProcessKeys($$$)
|
|
9538
9619
|
my $groups = $$tagInfo{Groups};
|
9539
9620
|
$$newInfo{Groups} = $groups ? { %$groups } : { };
|
9540
9621
|
$$newInfo{Groups}{$_} or $$newInfo{Groups}{$_} = $$tagTablePtr{GROUPS}{$_} foreach 0..2;
|
9622
|
+
# set Keys group. This is necessary for logic when reading the associated ItemList entry,
|
9623
|
+
# but note that the group name will be overridden by TAG_EXTRA G1 for tags in a track
|
9541
9624
|
$$newInfo{Groups}{1} = 'Keys';
|
9542
9625
|
} elsif ($tag =~ /^[-\w. ]+$/ or $tag =~ /\w{4}/) {
|
9543
9626
|
# create info for tags with reasonable id's
|
@@ -9584,6 +9667,43 @@ sub ProcessMetaKeys($$$)
|
|
9584
9667
|
return 1;
|
9585
9668
|
}
|
9586
9669
|
|
9670
|
+
#------------------------------------------------------------------------------
|
9671
|
+
# Identify trailers at specified offset from end of file
|
9672
|
+
# Inputs: 0) RAF reference, 1) Offset from end of file
|
9673
|
+
# Returns: Array ref to first trailer in linked list: 0) name of trailer,
|
9674
|
+
# 1) absolute offset to start of this trailer, 2) trailer length,
|
9675
|
+
# 3) ref to next trailer. Or undef if no trailer found, or error string on error
|
9676
|
+
# - file position is returned to its original location
|
9677
|
+
sub IdentifyTrailers($)
|
9678
|
+
{
|
9679
|
+
my $raf = shift;
|
9680
|
+
my ($trailer, $nextTrail, $buff, $type, $len);
|
9681
|
+
my $pos = $raf->Tell();
|
9682
|
+
my $offset = 0; # positive offset back from end of file
|
9683
|
+
while ($raf->Seek(-40-$offset, 2) and $raf->Read($buff, 40) == 40) {
|
9684
|
+
if (substr($buff, 8) eq '8db42d694ccc418790edff439fe026bf') {
|
9685
|
+
($type, $len) = ('Insta360', unpack('V',$buff));
|
9686
|
+
} elsif ($buff =~ /\&\&\&\&(.{4})$/) {
|
9687
|
+
($type, $len) = ('LigoGPS', Get32u(\$buff, 36));
|
9688
|
+
} elsif ($buff =~ /~\0\x04\0zmie~\0\0\x06.{4}([\x10\x18])(\x04)$/s or
|
9689
|
+
$buff =~ /~\0\x04\0zmie~\0\0\x0a.{8}([\x10\x18])(\x08)$/s)
|
9690
|
+
{
|
9691
|
+
my $oldOrder = GetByteOrder();
|
9692
|
+
SetByteOrder($1 eq "\x10" ? 'MM' : 'II');
|
9693
|
+
$type = 'MIE';
|
9694
|
+
$len = ($2 eq "\x04") ? Get32u(\$buff, 34) : Get64u(\$buff, 30);
|
9695
|
+
SetByteOrder($oldOrder);
|
9696
|
+
} else {
|
9697
|
+
last;
|
9698
|
+
}
|
9699
|
+
$trailer = [ $type , $raf->Tell() - $len, $len, $nextTrail ];
|
9700
|
+
$nextTrail = $trailer;
|
9701
|
+
$offset += $len;
|
9702
|
+
}
|
9703
|
+
$raf->Seek($pos,0) or return 'Seek error';
|
9704
|
+
return $trailer;
|
9705
|
+
}
|
9706
|
+
|
9587
9707
|
#------------------------------------------------------------------------------
|
9588
9708
|
# Process a QuickTime atom
|
9589
9709
|
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) optional tag table ref
|
@@ -9605,7 +9725,7 @@ sub ProcessMOV($$;$)
|
|
9605
9725
|
|
9606
9726
|
my $topLevel = not $$et{InQuickTime};
|
9607
9727
|
$$et{InQuickTime} = 1;
|
9608
|
-
$$et{HandlerType} = $$et{MetaFormat} = ''
|
9728
|
+
$$et{HandlerType} = $$et{MetaFormat} = $$et{MediaType} = '' if $topLevel;
|
9609
9729
|
|
9610
9730
|
unless (defined $$et{KeysCount}) {
|
9611
9731
|
$$et{KeysCount} = 0; # initialize ItemList key directory count
|
@@ -9631,15 +9751,10 @@ sub ProcessMOV($$;$)
|
|
9631
9751
|
}
|
9632
9752
|
($size, $tag) = unpack('Na4', $buff);
|
9633
9753
|
my $fast = $$et{OPTIONS}{FastScan} || 0;
|
9634
|
-
# check for Insta360 trailer
|
9754
|
+
# check for Insta360, LIGOGPSINFO or MIE trailer
|
9635
9755
|
if ($topLevel and not $fast) {
|
9636
|
-
|
9637
|
-
|
9638
|
-
substr($buff, 8) eq '8db42d694ccc418790edff439fe026bf')
|
9639
|
-
{
|
9640
|
-
$trailer = [ 'Insta360', $raf->Tell() - unpack('V',$buff) ];
|
9641
|
-
}
|
9642
|
-
$raf->Seek($pos,0) or return 0;
|
9756
|
+
$trailer = IdentifyTrailers($raf);
|
9757
|
+
$trailer and not ref $trailer and $et->Warn($trailer), return 0;
|
9643
9758
|
}
|
9644
9759
|
if ($dataPt) {
|
9645
9760
|
$verbose and $et->VerboseDir($$dirInfo{DirName});
|
@@ -9754,16 +9869,18 @@ sub ProcessMOV($$;$)
|
|
9754
9869
|
}
|
9755
9870
|
if ($isUserData and $$et{SET_GROUP1}) {
|
9756
9871
|
my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
|
9757
|
-
|
9758
|
-
|
9759
|
-
|
9760
|
-
|
9761
|
-
|
9762
|
-
|
9763
|
-
|
9764
|
-
|
9872
|
+
unless ($$tagInfo{SubDirectory}) {
|
9873
|
+
# add track name to UserData tags inside tracks
|
9874
|
+
$tag = $$et{SET_GROUP1} . $tag;
|
9875
|
+
if (not $$tagTablePtr{$tag} and $tagInfo) {
|
9876
|
+
my %newInfo = %$tagInfo;
|
9877
|
+
foreach ('Name', 'Description') {
|
9878
|
+
next unless $$tagInfo{$_};
|
9879
|
+
$newInfo{$_} = $$et{SET_GROUP1} . $$tagInfo{$_};
|
9880
|
+
$newInfo{$_} =~ s/^(Track\d+)Track/$1/; # remove duplicate "Track" in name
|
9881
|
+
}
|
9882
|
+
AddTagToTable($tagTablePtr, $tag, \%newInfo);
|
9765
9883
|
}
|
9766
|
-
AddTagToTable($tagTablePtr, $tag, \%newInfo);
|
9767
9884
|
}
|
9768
9885
|
}
|
9769
9886
|
# set flag to store additional information for ExtractEmbedded option
|
@@ -9830,7 +9947,7 @@ sub ProcessMOV($$;$)
|
|
9830
9947
|
# check for RIFF trailer (written by Auto-Vox dashcam)
|
9831
9948
|
if ($buff =~ /^(gpsa|gps0|gsen|gsea)...\0/s) { # (yet seen only gpsa as first record)
|
9832
9949
|
$et->VPrint(0, sprintf("Found RIFF trailer at offset 0x%x",$lastPos));
|
9833
|
-
if ($
|
9950
|
+
if ($ee) {
|
9834
9951
|
$raf->Seek(-8, 1) or last; # seek back to start of trailer
|
9835
9952
|
my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
|
9836
9953
|
ProcessRIFFTrailer($et, { RAF => $raf }, $tbl);
|
@@ -9980,6 +10097,7 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
|
|
9980
10097
|
}
|
9981
10098
|
}
|
9982
10099
|
if ($tagInfo) {
|
10100
|
+
my @found;
|
9983
10101
|
my $subdir = $$tagInfo{SubDirectory};
|
9984
10102
|
if ($subdir) {
|
9985
10103
|
my $start = $$subdir{Start} || 0;
|
@@ -10098,21 +10216,23 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
|
|
10098
10216
|
Index => $index,
|
10099
10217
|
Extra => sprintf(", Type='${type}', Flags=0x%x%s, Lang=0x%.4x",$flags,$str,$lang),
|
10100
10218
|
) if $verbose;
|
10101
|
-
|
10102
|
-
|
10103
|
-
|
10104
|
-
|
10105
|
-
|
10219
|
+
if (defined $value) {
|
10220
|
+
# use "Keys" in path instead of ItemList if this was defined by a Keys tag
|
10221
|
+
# (the only reason for this is to have "Keys" in the family 5 group name)
|
10222
|
+
# Note that the Keys group is specifically set by the ProcessKeys routine,
|
10223
|
+
# even though this tag would be in the ItemList table
|
10224
|
+
my $isKeys = $$tagInfo{Groups} && $$tagInfo{Groups}{1} && $$tagInfo{Groups}{1} eq 'Keys';
|
10225
|
+
$isKeys and $oldDir = $$et{PATH}[-1], $$et{PATH}[-1] = 'Keys';
|
10226
|
+
push @found, $et->FoundTag($langInfo, $value);
|
10227
|
+
$$et{PATH}[-1] = $oldDir if $isKeys;
|
10106
10228
|
}
|
10107
|
-
$et->FoundTag($langInfo, $value) if defined $value;
|
10108
|
-
$$et{PATH}[-1] = $oldDir if $isKey;
|
10109
10229
|
$pos += $len;
|
10110
10230
|
}
|
10111
10231
|
} elsif ($tag =~ /^\xa9/ or $$tagInfo{IText}) {
|
10112
10232
|
# parse international text to extract all languages
|
10113
10233
|
my $pos = 0;
|
10114
10234
|
if ($$tagInfo{Format}) {
|
10115
|
-
$et->FoundTag($tagInfo, ReadValue(\$val, 0, $$tagInfo{Format}, undef, length($val)));
|
10235
|
+
push @found, $et->FoundTag($tagInfo, ReadValue(\$val, 0, $$tagInfo{Format}, undef, length($val)));
|
10116
10236
|
$pos = $size;
|
10117
10237
|
}
|
10118
10238
|
for (;;) {
|
@@ -10177,7 +10297,7 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
|
|
10177
10297
|
$str = substr($val, $pos-$n-2, $n) . $str;
|
10178
10298
|
}
|
10179
10299
|
$langInfo = GetLangInfoQT($et, $tagInfo, $lang) if $lang;
|
10180
|
-
$et->FoundTag($langInfo || $tagInfo, $str);
|
10300
|
+
push @found, $et->FoundTag($langInfo || $tagInfo, $str);
|
10181
10301
|
$pos += $len;
|
10182
10302
|
}
|
10183
10303
|
} else {
|
@@ -10191,6 +10311,7 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
|
|
10191
10311
|
$$et{BASE} = $dataPos;
|
10192
10312
|
}
|
10193
10313
|
my $key = $et->FoundTag($tagInfo, $val);
|
10314
|
+
push @found, $key;
|
10194
10315
|
$$et{BASE} = $oldBase if defined $oldBase;
|
10195
10316
|
# decode if necessary (NOTE: must be done after RawConv)
|
10196
10317
|
if (defined $key and (not $format or $format =~ /^string/) and
|
@@ -10206,6 +10327,14 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
|
|
10206
10327
|
}
|
10207
10328
|
}
|
10208
10329
|
}
|
10330
|
+
# tweak family 1 group names for Keys/ItemList/UserData tags in a track
|
10331
|
+
if ($$et{SET_GROUP1} and ($dirID eq 'ilst' or $dirID eq 'udta') and @found) {
|
10332
|
+
my $type = $trackPath{join '-', @{$$et{PATH}}};
|
10333
|
+
if ($type) {
|
10334
|
+
my $grp = ($avType{$$et{MediaType}} || $$et{SET_GROUP1}) . $type;
|
10335
|
+
defined and $et->SetGroup($_, $grp) foreach @found;
|
10336
|
+
}
|
10337
|
+
}
|
10209
10338
|
}
|
10210
10339
|
} else {
|
10211
10340
|
$et->VerboseInfo($tag, $tagInfo,
|
@@ -10218,11 +10347,12 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
|
|
10218
10347
|
last;
|
10219
10348
|
}
|
10220
10349
|
}
|
10350
|
+
$$et{MediaType} = '' if $tag eq 'trak'; # reset track type at end of track
|
10221
10351
|
$dataPos += $size + 8; # point to start of next atom data
|
10222
10352
|
last if $dirEnd and $dataPos >= $dirEnd; # (note: ignores last value if 0 bytes)
|
10223
10353
|
$lastPos = $raf->Tell() + $dirBase;
|
10224
10354
|
if ($trailer and $lastPos >= $$trailer[1]) {
|
10225
|
-
$et->Warn(sprintf('%s trailer at offset 0x%x', @$trailer), 1);
|
10355
|
+
$et->Warn(sprintf('%s trailer at offset 0x%x (%d bytes)', @$trailer[0..2]), 1);
|
10226
10356
|
last;
|
10227
10357
|
}
|
10228
10358
|
$raf->Read($buff, 8) == 8 or last;
|
@@ -10273,7 +10403,33 @@ QTLang: foreach $tag (@{$$et{QTLang}}) {
|
|
10273
10403
|
# process item information now that we are done processing its 'meta' container
|
10274
10404
|
HandleItemInfo($et) if $topLevel or $dirID eq 'meta';
|
10275
10405
|
|
10276
|
-
|
10406
|
+
# process linked list of trailers
|
10407
|
+
for (; $trailer; $trailer=$$trailer[3]) {
|
10408
|
+
next if $lastPos > $$trailer[1]; # skip if we have already processed this as an atom
|
10409
|
+
last unless $raf->Seek($$trailer[1], 0);
|
10410
|
+
if ($$trailer[0] eq 'LigoGPS' and $raf->Read($buff, 8) == 8 and $buff =~ /skip$/) {
|
10411
|
+
$ee or $et->Warn('Use the ExtractEmbedded option to decode timed GPS',3), next;
|
10412
|
+
my $len = Get32u(\$buff, 0) - 16;
|
10413
|
+
if ($len > 0 and $raf->Read($buff, $len) == $len and $buff =~ /^LIGOGPSINFO\0/) {
|
10414
|
+
my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
|
10415
|
+
my %dirInfo = ( DataPt => \$buff, DataPos => $$trailer[1] + 8, DirName => 'LigoGPSTrailer' );
|
10416
|
+
Image::ExifTool::LigoGPS::ProcessLigoGPS($et, \%dirInfo, $tbl);
|
10417
|
+
} else {
|
10418
|
+
$et->Warn('Unrecognized data in LigoGPS trailer');
|
10419
|
+
}
|
10420
|
+
} elsif ($$trailer[0] eq 'Insta360' and $ee) {
|
10421
|
+
# process Insta360 trailer if it exists
|
10422
|
+
$raf->Seek(0, 2) or $et->Warn('Seek error'), last;
|
10423
|
+
my $offset = $raf->Tell() - $$trailer[1] - $$trailer[2];
|
10424
|
+
ProcessInsta360($et, { RAF => $raf, DirName => $$trailer[0], Offset => $offset });
|
10425
|
+
} elsif ($$trailer[0] eq 'MIE') {
|
10426
|
+
require Image::ExifTool::MIE;
|
10427
|
+
Image::ExifTool::MIE::ProcessMIE($et, { RAF => $raf, DirName => 'MIE', Trailer => 1 });
|
10428
|
+
}
|
10429
|
+
}
|
10430
|
+
# brute force scan for metadata embedded in media data
|
10431
|
+
# (and process Insta360 trailer if it exists)
|
10432
|
+
ScanMediaData($et) if $ee and $topLevel;
|
10277
10433
|
|
10278
10434
|
# restore any changed options
|
10279
10435
|
$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
|
#------------------------------------------------------------------------------
|
@@ -3154,16 +3154,18 @@ sub ProcessInsta360($;$)
|
|
3154
3154
|
my $verbose = $et->Options('Verbose');
|
3155
3155
|
my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
|
3156
3156
|
my $fileEnd = $raf->Tell();
|
3157
|
+
my $trailEnd = $fileEnd - $offset;
|
3157
3158
|
my $trailerLen = unpack('x38V', $buff);
|
3158
|
-
$trailerLen > $
|
3159
|
+
$trailerLen > $trailEnd and $et->Warn('Bad Insta360 trailer size'), return 0;
|
3159
3160
|
if ($dirInfo) {
|
3160
3161
|
$$dirInfo{DirLen} = $trailerLen;
|
3161
|
-
$$dirInfo{DataPos} = $
|
3162
|
+
$$dirInfo{DataPos} = $trailEnd - $trailerLen;
|
3162
3163
|
if ($$dirInfo{OutFile}) {
|
3163
3164
|
if ($$et{DEL_GROUP}{Insta360}) {
|
3164
3165
|
++$$et{CHANGED};
|
3166
|
+
return 1;
|
3165
3167
|
# just copy the trailer when writing
|
3166
|
-
} elsif ($trailerLen > $
|
3168
|
+
} elsif ($trailerLen > $trailEnd or not $raf->Seek($$dirInfo{DataPos}, 0) or
|
3167
3169
|
$raf->Read(${$$dirInfo{OutFile}}, $trailerLen) != $trailerLen)
|
3168
3170
|
{
|
3169
3171
|
return 0;
|
@@ -3181,7 +3183,7 @@ sub ProcessInsta360($;$)
|
|
3181
3183
|
|
3182
3184
|
my $unknown = $et->Options('Unknown');
|
3183
3185
|
# position relative to end of trailer (avoids using large offsets for files > 2 GB)
|
3184
|
-
my $epos = -78
|
3186
|
+
my $epos = -78;
|
3185
3187
|
my ($i, $p);
|
3186
3188
|
$$et{SET_GROUP0} = 'Trailer';
|
3187
3189
|
$$et{SET_GROUP1} = 'Insta360';
|
@@ -3190,7 +3192,7 @@ sub ProcessInsta360($;$)
|
|
3190
3192
|
for (;;) {
|
3191
3193
|
my ($id, $len) = unpack('vV', $buff);
|
3192
3194
|
($epos -= $len) + $trailerLen < 0 and last;
|
3193
|
-
$raf->Seek($epos, 2) or last;
|
3195
|
+
$raf->Seek($epos-$offset, 2) or last;
|
3194
3196
|
if ($verbose) {
|
3195
3197
|
$et->VPrint(0, sprintf("Insta360 Record 0x%x (offset 0x%x, %d bytes):\n", $id, $fileEnd + $epos, $len));
|
3196
3198
|
}
|
@@ -3218,7 +3220,7 @@ sub ProcessInsta360($;$)
|
|
3218
3220
|
$dlen = 20;
|
3219
3221
|
}
|
3220
3222
|
}
|
3221
|
-
$raf->Seek($epos, 2) or last;
|
3223
|
+
$raf->Seek($epos-$offset, 2) or last;
|
3222
3224
|
}
|
3223
3225
|
} elsif ($id == 0x200) {
|
3224
3226
|
$dlen = $len;
|
@@ -3347,8 +3349,8 @@ sub ProcessInsta360($;$)
|
|
3347
3349
|
} else {
|
3348
3350
|
($epos -= 6) + $trailerLen < 0 and last; # step back to previous record
|
3349
3351
|
}
|
3350
|
-
$raf->Seek($epos, 2) or last;
|
3351
|
-
$raf->Read($buff, 6) == 6 or last;
|
3352
|
+
$raf->Seek($epos-$offset, 2) or last; # seek to start of next footer
|
3353
|
+
$raf->Read($buff, 6) == 6 or last; # read footer
|
3352
3354
|
}
|
3353
3355
|
delete $$et{DOC_NUM};
|
3354
3356
|
SetByteOrder('MM');
|
@@ -3607,8 +3609,6 @@ sub ScanMediaData($)
|
|
3607
3609
|
$et->VPrint(0, "--------------------------\n");
|
3608
3610
|
$$et{INDENT} = substr $$et{INDENT}, 0, -2;
|
3609
3611
|
}
|
3610
|
-
# process Insta360 trailer if it exists
|
3611
|
-
ProcessInsta360($et);
|
3612
3612
|
}
|
3613
3613
|
|
3614
3614
|
1; # end
|
@@ -905,7 +905,10 @@ numerical, and generated automatically otherwise.
|
|
905
905
|
Deletable : [Writable SubDirectory's only] Overrides internal test for
|
906
906
|
metadata types with permanent directories (currently QuickTime
|
907
907
|
and Jpeg2000), allowing the tag containing these directories
|
908
|
-
to be deleted
|
908
|
+
to be deleted. Note that either the tag's SubDirectory
|
909
|
+
DirName (which defaults to the TagName if not specified) or
|
910
|
+
the family 0 group name of the SubDirectory TagTable must
|
911
|
+
match a deletable group name.
|
909
912
|
|
910
913
|
OffsetPair : Used in EXIF table to specify the tagID for the corresponding
|
911
914
|
offset or length tag.
|
@@ -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.70';
|
34
34
|
|
35
35
|
sub ConvertTimecode($);
|
36
36
|
sub ProcessSGLT($$$);
|
@@ -1319,6 +1319,11 @@ my %code2charset = (
|
|
1319
1319
|
Name => 'ImageWidth',
|
1320
1320
|
Format => 'int16u',
|
1321
1321
|
Priority => 0,
|
1322
|
+
# add " (lossless)" to FileType since image has a VP8L (lossless) chunk
|
1323
|
+
RawConv => q{
|
1324
|
+
$self->OverrideFileType($$self{VALUE}{FileType} . ' (lossless)', undef, 'webp');
|
1325
|
+
return $val;
|
1326
|
+
},
|
1322
1327
|
ValueConv => '($val & 0x3fff) + 1',
|
1323
1328
|
},
|
1324
1329
|
2 => {
|
@@ -1327,6 +1332,11 @@ my %code2charset = (
|
|
1327
1332
|
Priority => 0,
|
1328
1333
|
ValueConv => '(($val >> 6) & 0x3fff) + 1',
|
1329
1334
|
},
|
1335
|
+
4 => {
|
1336
|
+
Name => 'AlphaIsUsed',
|
1337
|
+
Mask => 0x10,
|
1338
|
+
PrintConv => { 0 => 'No', 1 => 'Yes' },
|
1339
|
+
},
|
1330
1340
|
);
|
1331
1341
|
|
1332
1342
|
# WebP extended info (ref 14)
|
@@ -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
|
}
|